Angular 订阅observable后未定义的字符串[]

Angular 订阅observable后未定义的字符串[],angular,rxjs,Angular,Rxjs,下面的代码给出了一个运行时错误,因为top5Ids未定义。我真的不明白,当我正确地订阅observable并从订阅方next方法内部设置top5Ids变量时,为什么这个变量没有定义 cryptoService类只是返回http.get()函数的结果,该函数是可观察的 export class HomeComponent implements OnInit { cryptoDetails: CryptoDetail[]; top5Ids: string[]; constructor

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

cryptoService类只是返回http.get()函数的结果,该函数是可观察的

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

}
导出类HomeComponent实现OnInit{
cryptoDetails:CryptoDetail[];
top5id:string[];
构造函数(私有加密服务:加密服务){}
恩戈尼尼特(){
this.cryptoDetails=新数组();
this.getTop5Crypto();
this.getCryptoData(this.top5Ids);
const source=interval(5000).subscribe(val=>this.getCryptoData(this.top5Ids))
}
getCryptoData(ID:字符串[]){
this.cryptoDetails=[];
log(“获取加密”)
for(让id中的id){
this.cryptoService.getCryptoInfo(id).subscribe(res=>{
设data=res.data;
let cryptoDetail=新的cryptoDetail(data.id、data.rateUsd、data.symbol);
this.cryptoDetails.push(cryptoDetail);
})
}
this.cryptoDetails.sort();
}
getTop5Crypto():void{
让top5CryptoIds:string[]=[];
this.cryptoService.getCryptoMarkets().pipe(take(1)).subscribe(res=>{
设data=res.data;
for(设i=0;i<6;i++){
top5CryptoIds.push(数据[i].baseId)
}
this.top5Ids=top5CryptoIds;
});
}
}

在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)),
    );
  }
}
导出类HomeComponent实现OnInit{
cryptoDetails:CryptoDetail[];
top5Ids$:可观察;
构造函数(私有加密服务:加密服务){}
恩戈尼尼特(){
this.top5Ids$=this.getTop5Crypto();
计时器(0,5000)。管道(
switchMap(()=>this.top5Ids$),
switchMap((top5Ids)=>this.getCryptoData(top5Ids))
).订阅((加密详细信息)=>{
this.cryptoDetails=cryptoDetails;
})
}
getCryptoData(ID:string[]):可观察{
返回forkJoin(id.map(id=>this.cryptoService.getCryptoInfo(id)))
.烟斗(
map(responses=>responses.map(r=>newcryptodetail(r.data.id,r.data.rateud,r.data.symbol)).sort())
);
}
getTop5Crypto():可观察{
返回此.cryptoService.getCryptoMarkets()管道(
以(1)为例,
map((res:any)=>res.data.filter((d,i)=>i<5.map(d=>d.baseId)),
);
}
}
函数式反应式编程的思想是,我们编写代码,对DOM事件和Http响应等事件作出反应,然后通过一系列(理想情况下是纯)函数将转换应用于与这些事件相关联的数据

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


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

您订阅了observable,因为它是异步发射的。即在方法getTop5Crypto()返回很久之后。这就像你发送电子邮件一样:你不能期望在发送电子邮件后立即从响应中提取数据。只有在电子邮件客户端通知你响应已返回时,你才能这样做。@JBNizet太好了。但是我如何修复这个代码呢?例如,通过阅读patjim给你的答案。但最重要的是,通过稍微远离这段代码,阅读和练习异步,RxJS。回答得不错!这是反应的方式。谢谢!。稍有改进-无需在
getCryptoData
中执行(ids)的
。请看我的编辑。谢谢你的回答!我现在正在努力。但是,对于getTop5Crypto中的下一行,{}
类型上不存在
属性“data”<代码>映射(res=>res.data.filter((d,i)=>i<5.map(d=>d.baseId)),
,这使我无法编译类型脚本无法推断
res
的类型,您需要明确地将其键入
any
-请参阅我的编辑。