如何在 angular 中返回 Observable/http/async 调用的响应?

How do I return the response from an Observable/http/async call in angular?

提问人:eko 提问时间:3/28/2017 最后编辑:eko 更新时间:10/27/2022 访问量:48352

问:

我有返回一个可观察对象的服务,该可观察对象向我的服务器发出 http 请求并获取数据。我想使用这些数据,但我最终总是得到.怎么了?undefined

服务

@Injectable()
export class EventService {

  constructor(private http: Http) { }

  getEventList(): Observable<any>{
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this.http.get("http://localhost:9999/events/get", options)
                .map((res)=> res.json())
                .catch((err)=> err)
  }
}

元件:

@Component({...})
export class EventComponent {

  myEvents: any;

  constructor( private es: EventService ) { }

  ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
        });

    console.log(this.myEvents); //This prints undefined!
  }
}

我检查了如何返回异步调用的响应? 发布但找不到解决方案

JavaScript Angular 异步 TypeScript 可观察对象

评论

4赞 n00dl3 4/14/2017
这是一个很好的观点,可以强调这样一个事实,即不可能将异步操作转换为同步操作。
1赞 eko 4/15/2017
@n00dl3 感谢您的提示!我试图在“你不应该做什么:”部分中解释。随意编辑它。
1赞 Heretic Monkey 4/25/2020
这回答了你的问题吗?如何返回异步调用的响应?
1赞 eko 4/25/2020
@HereticMonkey该帖子已记入答案
2赞 eko 4/25/2020
@HereticMonkey是的,它确实如此,因为它是特定于框架的,它是可观察的。当我复制 angular/rxjs、带有该答案的异步问题时,人们感到困惑并说 ing observable 不起作用;这对回答者来说是显而易见的,但对你来说显然不是。此外,我认为在答案中给予信任就足够了,但我也会改变问题。thenangular

答:

135赞 eko 3/28/2017 #1

原因:

原因是您正在执行异步操作。这意味着完成该方法需要一些时间(主要取决于您的网络速度)。undefinedgetEventList

因此,让我们看一下 http 调用。

this.es.getEventList()

在你实际发出(“触发”)你的 http 请求后,你将等待响应。在等待期间,javascript 将执行此代码下面的行,如果它遇到同步赋值/操作,它将立即执行它们。subscribe

所以在订阅并等待响应之后,getEventList()

console.log(this.myEvents);

行将立即执行。它的值是在响应从服务器到达之前(或者你首先初始化它的任何内容)。undefined

这类似于执行以下操作:

ngOnInit(){
    setTimeout(()=>{
        this.myEvents = response;
    }, 5000);

    console.log(this.myEvents); //This prints undefined!
}

**溶液:** >那么我们如何克服这个问题呢?我们将使用回调函数,即“订阅”方法。因为当数据从服务器到达时,它将在响应的“订阅”中。

因此,将代码更改为:

this.es.getEventList()
    .subscribe((response)=>{
        this.myEvents = response;
        console.log(this.myEvents); //<-- not undefined anymore
    });

将打印响应。.过了一段时间。


**你应该做什么:**

除了记录回复之外,可能还有很多事情要做;当数据到达时,您应该在回调(函数内部)执行所有这些操作。subscribe

另一件值得一提的事情是,如果你来自后台,回调对应于 observables。Promisethensubscribe


**你不应该做什么:**

不应尝试将异步操作更改为同步操作(不是这样)。我们使用异步操作的原因之一是不要让用户等待操作完成,而他们可以在该时间段内执行其他操作。假设您的一个异步操作需要 3 分钟才能完成,如果我们没有异步操作,那么接口将冻结 3 分钟。


推荐阅读:

这个答案的原始功劳在于:如何从异步调用返回响应?

但是在 angular2 版本中,我们被引入了打字稿和可观察对象,所以这个答案希望涵盖了使用可观察对象处理异步请求的基础知识。

评论

5赞 Jonas Praem 12/22/2018
当提问者在帖子的完全相同时间回答问题时。这是一个停下来写博客文章的好地方
8赞 Amit Chigadani 12/23/2018
@JonasPraem 没错,但在 Stackoverflow 上分享知识并没有错。正如你所看到的选票数量,它确实帮助了这里的许多人,并将继续这样做。
2赞 Tanzeel 8/22/2020
我的问题有点相似。但这个答案也解决了这个问题。:-)
2赞 luukgruijs 3/28/2017 #2

此外,请确保将响应映射到 json 输出。否则,它将返回纯文本。你这样做是这样的:

getEventList(): Observable<any> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });

return this.http.get("http://localhost:9999/events/get", options)
            .map((res)=>{ return res.json();}) <!-- add call to json here
            .catch((err)=>{return err;})
}
15赞 RAVI PATEL 5/20/2017 #3

在 angular/javascript 中进行 http 调用是异步操作。 因此,当您进行 http 调用时,它将分配新线程来完成此调用,并使用另一个线程开始执行下一行。 这就是为什么你得到未定义的价值。 因此,请进行以下更改以解决此问题

this.es.getEventList()  
      .subscribe((response)=>{  
       this.myEvents = response;  
        console.log(this.myEvents); //<-this become synchronous now  
    });
1赞 Harshit 6/10/2017 #4

您可以简单地尝试此方法-

let headers = new Headers({'Accept': 'application/json'});
let options = new RequestOptions({headers: headers});

return this.http
    .get(this.yourSearchUrlHere, options) // the URL which you have defined
    .map((res) => {
        res.json(); // using return res.json() will throw error
    }
    .catch(err) => {
        console.error('error');
    }
7赞 Kliment Ru 2/19/2018 #5

如果仅在模板中使用 myEvents,则可以使用 asyncPipe

这里的例子是 asyncPipe 和 Angular4 HttpClient 的例子

评论

1赞 San Jay Falcon 12/3/2018
并且 (!) 使用此实现,无需取消订阅订阅:medium.com/@sub.metu/...
0赞 AT82 4/22/2019
@SanJayFalcon 由于这是一个 http 请求,因此无需退订。
3赞 Kooldandy 9/23/2018 #6

Observables 是惰性的,因此您必须订阅才能获得值。您在代码中正确订阅了它,但同时将输出记录在“subscribe”块之外。这就是为什么它是“未定义”的。

ngOnInit() {
  this.es.getEventList()
    .subscribe((response) => {
        this.myEvents = response;
    });

  console.log(this.myEvents); //Outside the subscribe block 'Undefined'
}

因此,如果您将其记录在订阅块中,那么它将正确记录响应。

ngOnInit(){
  this.es.getEventList()
    .subscribe((response)=>{
        this.myEvents = response;
        console.log(this.myEvents); //Inside the subscribe block 'http response'
    });
}
3赞 Suneet Bansal 3/4/2019 #7

这里的问题是,您正在初始化到哪个异步块中,而您只是在块外执行。 因此,在初始化之前被调用。this.myEventssubscribe()console.log()subscribe()console.log()this.myEvents

请将您的控制台.log 代码也移动到 subscribe() 中,您就完成了。

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
            console.log(this.myEvents);
        });
  }
3赞 NhutLe 3/9/2019 #8

结果是未定义的,因为 angular process async . 您可以尝试如下操作:

async ngOnInit(){
    const res = await this.es.getEventList();
    console.log(JSON.stringify(res));
}
2赞 Hitech Hitesh 12/16/2019 #9

未定义,因为此处的值是在从上述订阅服务调用设置来自服务的任何数据之前记录的。因此,您必须等到ajax调用完成,然后从响应数据中设置数据。

getEventList(): Observable<any>{
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this.http.get("http://localhost:9999/events/get", options)
                .map((res)=> res.json())
                .catch((err)=> err)
  }

这里在 subscribe 方法中制作控制台日志,该方法将在 myEvents 变量中设置数据时制作日志。

ngOnInit(){
    this.es.getEventList()
        .subscribe((response)=>{
            this.myEvents = response;
     // This prints the value from the response
    console.log(this.myEvents)
        }); 
  }
2赞 Rajesh Kumar 8/5/2021 #10

为此,您有 2 个选项:

假设我们有一个服务,它返回运输详细信息数组:

  getShippingPrices(): Observable<IShippingDetails[]> {
    return this.http.get<IShippingDetails[]>('/assets/shipping.json');
  }

1.使用异步管道:当您只想在模板中显示结果时,简单的方法

在组件类中直接将 observable 赋值给变量:

export class ShippingComponent implements OnInit {
  shipOptions1 = this.cartService.getShippingPrices();
  constructor(private cartService: CartService) {}

  ngOnInit() {}
}

然后在模板中使用异步管道:

<div *ngFor="let s of shipOptions1 |async">
  <label>{{s.type}}</label>
</div>

参考:检查此 URL 中的第 4 点 https://angular.io/start/start-data#configuring-the-shippingcomponent-to-use-cartservice

2.使用订阅:当你想操作它或想在响应上/从响应中执行一些业务逻辑时

export class ShippingComponent implements OnInit {
  shipOptions2: IShippingDetails[] = [];
  constructor(private cartService: CartService) {}

  ngOnInit() {
    this.cartService.getShippingPrices().subscribe(response => {
      this.shipOptions2 = response;
      //console.log(this.myEvents);
      //All other code using shipOptions2
    });
  }
}