【问题标题】:How do I return the response from an Observable/http/async call in angular?如何以角度返回 Observable/http/async 调用的响应?
【发布时间】:2017-08-20 17:01:48
【问题描述】:

我有一个服务,它返回一个 observable,它向我的服务器发出 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!
  }
}

我查看了How do I return the response from an asynchronous call? 的帖子,但找不到解决方案

【问题讨论】:

  • 这将是一个很好的重点,强调不可能将异步操作转换为同步操作。
  • @n00dl3 感谢您的提示!我试图在“你不应该做的事情:”部分进行解释。随意编辑它。
  • @HereticMonkey 该帖子已被记入答案
  • @HereticMonkey 是的,因为它是特定于框架的,它是可观察的。当我用那个答案复制 angular/rxjs,异步问题时,人们感到困惑并说thening observable 不起作用;这对angular 回答者来说是显而易见的,但对你来说显然不是。我也认为在答案中给予信任就足够了,但我也会改变问题。

标签: javascript angular asynchronous typescript observable


【解决方案1】:

原因:

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

让我们看看 http 调用。

this.es.getEventList()

在您使用 subscribe 实际发出(“触发”)您的 http 请求后,您将等待响应。在等待期间,javascript 将执行这段代码下面的行,如果遇到同步分配/操作,它会立即执行它们。

所以在订阅getEventList()并等待回复后,

console.log(this.myEvents);

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

类似于做:

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

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


解决方案:

那么我们如何克服这个问题呢?我们将使用回调函数,即subscribe 方法。因为当数据从服务器到达时,它会在 subscribe 中并带有响应。

所以把代码改成:

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

将打印响应.. 一段时间后。


你应该做什么:

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

另外要提到的是,如果你来自 Promise 背景,then 回调对应于带有 observables 的 subscribe


你不应该做的事情:

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


推荐阅读:

此答案的原始功劳归于:How do I return the response from an asynchronous call?

但是随着 angular2 版本的发布,我们被引入了 typescript 和 observables,所以这个答案有望涵盖使用 observables 处理异步请求的基础知识。

【讨论】:

  • 当提问者在发帖的同时回答问题时.. 这是一个停下来写博客文章的好地方
  • @JonasPraem 没错,但是在 Stackoverflow 上分享知识并没有错。正如您所看到的投票数,它确实帮助了这里的许多人,并且它将继续这样做。
  • 我的问题有点类似。但是这个答案也解决了这个问题。 :-)
【解决方案2】:

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

this.es.getEventList()  
      .subscribe((response)=>{  
       this.myEvents = response;  
        console.log(this.myEvents); //<-this become synchronous now  
    });

【讨论】:

    【解决方案3】:

    如果你只在模板中使用 myEvents,你可以使用asyncPipe

    这里是 asyncPipe 和 Angular4 HttpClient example 的示例

    【讨论】:

    • 并且 (!) 使用此实现不需要取消订阅订阅:medium.com/@sub.metu/…
    • @SanJayFalcon 因为这是http请求,所以不需要退订。
    【解决方案4】:

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

    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'
        });
    }
    

    【讨论】:

      【解决方案5】:

      这里的问题是,您正在将 this.myEvents 初始化为 subscribe(),这是一个异步块,而您正在执行 console.log() 刚刚超出 subscribe() 块。 所以console.log()this.myEvents 被初始化之前被调用。

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

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

      【讨论】:

        【解决方案6】:

        结果未定义,因为角度处理异步。 您可以尝试如下:

        async ngOnInit(){
            const res = await this.es.getEventList();
            console.log(JSON.stringify(res));
        }
        

        【讨论】:

          【解决方案7】:

          还要确保将响应映射到 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;})
          }
          

          【讨论】:

            【解决方案8】:

            未定义,因为此处的值是在从上述订阅服务调用设置任何来自服务的数据之前记录的。因此,您必须等到 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)
                    }); 
              }
            

            【讨论】:

              【解决方案9】:

              你可以试试这个方法-

              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');
                  }
              

              【讨论】:

                【解决方案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
                    });
                  }
                }
                
                

                【讨论】:

                  猜你喜欢
                  • 2018-03-19
                  • 2018-02-20
                  • 1970-01-01
                  • 2016-07-26
                  • 1970-01-01
                  • 2018-08-28
                  相关资源
                  最近更新 更多