【问题标题】:Undefined string[] after subscribing to observable订阅 observable 后未定义的字符串 []
【发布时间】:2019-07-04 20:31:27
【问题描述】:

以下代码给出了运行时错误,因为 top5Ids 未定义。当我正确订阅 observable 并从订阅者 next 方法内部设置 top5Ids 变量时,我真的不明白为什么这个变量是未定义的。

cryptoService 类只是返回 http.get() 函数的结果,它是一个 observable。

export class HomeComponent implements OnInit {

  cryptoDetails: CryptoDetail[];
  top5Ids: string[];

  constructor(private cryptoService: CryptoService) { }

  ngOnInit() {
    this.cryptoDetails = new Array();

    this.getTop5Crypto();
    this.getCryptoData(this.top5Ids);

    const source = interval(5000).subscribe(val => this.getCryptoData(this.top5Ids))

  }

  getCryptoData(ids: string[]){
    this.cryptoDetails = [];
    console.log("get crypto")
    for(let id of ids){
      this.cryptoService.getCryptoInfo(id).subscribe(res => {
        let data = res.data;
        let cryptoDetail = new CryptoDetail(data.id, data.rateUsd, data.symbol);
        this.cryptoDetails.push(cryptoDetail);

      })
    }

    this.cryptoDetails.sort();
  }

  getTop5Crypto() : void {

    let top5CryptoIds : string[] = [];
    this.cryptoService.getCryptoMarkets().pipe(take(1)).subscribe(res => {
      let data = res.data;
      for(let i = 0; i < 6; i++) {
        top5CryptoIds.push(data[i].baseId)
      }
      this.top5Ids = top5CryptoIds;
    });
  }

}

【问题讨论】:

  • 您订阅了一个可观察对象,因为它异步发出。 IE。在方法 getTop5Crypto() 返回很久之后。就像您发送电子邮件一样:您不能指望在发送电子邮件后立即从响应中提取数据。只有在电子邮件客户端通知您回复已返回时,您才能这样做。
  • @JBNizet 太好了。但是如何修复此代码?
  • 例如,通过阅读您从 patjim 得到的答案。但最重要的是,暂时远离这段代码,阅读和练习异步 RxJS。

标签: angular rxjs


【解决方案1】:

在 Angular 中,最好完全采用函数式反应式编程。

你可以改写你的代码如下:

export class HomeComponent implements OnInit {

  cryptoDetails: CryptoDetail[];
  top5Ids$: Observable<string[]>;

  constructor(private cryptoService: CryptoService) { }

  ngOnInit() {
    this.top5Ids$ = this.getTop5Crypto();

    timer(0, 5000).pipe(
      switchMap(() => this.top5Ids$),
      switchMap((top5Ids) => this.getCryptoData(top5Ids))
    ).subscribe((cryptoDetails) => {
      this.cryptoDetails = cryptoDetails;
    })

  }


  getCryptoData(ids: string[]): Observable<CryptoDetail[]> {
    return forkJoin(ids.map(id => this.cryptoService.getCryptoInfo(id)))
            .pipe(
              map(responses => responses.map(r => new CryptoDetail(r.data.id, r.data.rateUsd, r.data.symbol)).sort())
            );
  }

  getTop5Crypto() : Observable<string[]> {
    return this.cryptoService.getCryptoMarkets().pipe(
      take(1),
      map((res: any) => res.data.filter((d, i) => i < 5).map(d => d.baseId)),
    );
  }
}

函数式反应式编程的理念是,我们编写代码对诸如 DOM 事件和 Http 响应之类的事物做出反应,然后通过一系列(理想情况下是纯的)函数对与这些事件关联的数据进行转换。

我们尽量避免创建手动订阅,除非在管道末端(如果我们使用 Angular 异步管道,有时甚至不会)。通过这种方式,我们获得了一个很好的可预测的异步事件管道,并避免了诸如竞争条件之类的问题。

需要注意的是,要做这些事情,你必须对 RxJS 有深刻的理解。

【讨论】:

  • 不错的答案!这是反应的方式。欣赏它!一点改进 - 无需在 getCryptoData 中执行 of(ids)。查看我的编辑。
  • 感谢您的回答!我现在正在处理它。但是我得到Property 'data' does not exist on type {} getTop5Crypto 中的以下行; map(res =&gt; res.data.filter((d, i) =&gt; i &lt; 5).map(d =&gt; d.baseId)),,这使我无法编译
  • 那是 TypeScript 无法推断 res 的类型,您需要明确地将其键入为 any - 请参阅我的编辑。
猜你喜欢
  • 2020-09-04
  • 2018-07-22
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 2017-11-30
  • 1970-01-01
  • 1970-01-01
  • 2019-10-25
相关资源
最近更新 更多