【问题标题】:Angular/TS: Asynchronous HTTP call inside forEach LoopAngular/TS:forEach 循环内的异步 HTTP 调用
【发布时间】:2019-07-31 19:00:32
【问题描述】:

有没有办法让 Typescript 中的 forEach 循环等待,以便像 http 调用这样的异步代码可以正常完成。

假设我在一个 Angular 组件中有三个数组 a[]、b[] 和 c[]。 共有三个功能,后两个依赖于之前功能的完成。

loadA(){
this.http.get<a[]>(http://getA).subscribe(a=> this.a = a, 
()=>loadB());
}

loadB(){
this.a.forEach((res, index)=>{
this.http.get<b[]>('http://getBbyA/res').subscribe(b=> this.b.push(...b),
()=> {
if(index===this.a.length-1){
loadC());
}
}
});

loadC(){
this.b.forEach(res=>{
this.http.get<c[]>('http://getCbyB/res').subscribe(c=> this.c.push(...c));
});
}

现在对于第二种方法,forEach 循环使得在 b[] 数组正确加载从 http 调用获取的数据后调用 loadC() 函数变得不可预测。如何使 loadB() 中的 forEach 循环等待获取所有 http 结果然后调用 loadC() 以避免不可预测性?

更新(使用 RxJs 运算符):

我在我的项目中尝试了以下方法:

loadData(): void {
    this.http.post<Requirement[]>(`${this.authService.serverURI}/requirement/get/requirementsByDeal`, this.dealService.deal).pipe(
      concatAll(), // flattens the array from the http response into Observables
      concatMap(requirement => this.http.post<ProductSet[]>(`${this.authService.serverURI}/productSet/getProductSetsByRequirement`, requirement).pipe( // loads B for each value emitted by source observable. Source observable emits all elements from LoadA-result in this case
        concatAll(), // flattens the array from the http response of loadB
        concatMap(pSet => this.http.post<Product[]>(`${this.authService.serverURI}/product/get/productsByProductSet`, pSet).pipe( // foreach element of LoadB Response load c
          map(product => ({requirement, pSet, product})) // return a object of type { a: LoadAResult, b: LoadBResult, c: LoadCResult}
        ))
      )),
      toArray()
    ).subscribe((results: { requirement: Requirement, productSet: ProductSet, product: Product }[] => {
      results.forEach(result => {
        this.requirements.push(...result.requirement);
        this.productSets.push(...result.productSet);
        this.products.push(...result.product);
      });
    }));
  } 

但我仍然遇到一些错误 (TS2345)。我哪里出错了?

【问题讨论】:

标签: angular typescript asynchronous foreach


【解决方案1】:

有几种方法可以实现这一点。我建议使用 rxJs 运算符 concatAllconcatMap 以便按顺序触发 httpCalls。我准备了一个快速的sn-p。 它首先加载 A,然后为 A 中的每个元素加载 B,然后为 B 中的每个元素加载 C。您可能需要根据需要调整结果的聚合

load(): void {
this.http.get<LoadAResult[]>("/loadA").pipe(
  concatAll(), // flattens the array from the http response into Observables
  concatMap(a => this.http.get<LoadBResult[]>("/loadB").pipe( // loads B for each value emitted by source observable. Source observable emits all elements from LoadA-result in this case
    concatAll(), // flattens the array from the http response of loadB
    concatMap(b => this.http.get<LoadCResult[]>("/loadC").pipe( // foreach element of LoadB Response load c
      map(c => ({a, b,c })) // return a object of type { a: LoadAResult, b: LoadBResult, c: LoadCResult}
    ))
  )),
  toArray()
).subscribe((results: { a: LoadAResult, b: LoadBResult, c: LoadCResult[]}[]) => {
  // do something with results
}));

}

【讨论】:

  • 嗨,我已经在我的项目中尝试过你的建议(请参阅问题中的更新),它不起作用。我收到一些错误(TS2345)。 "((results: { requirements: Requirement, productSet: ProductSet, product: Product }[] => {" 是否有任何语法错误?
  • 缺少 )。 sry :) 另请注意,如果您只是将元素推送到数组中,那么您将在那里有很多重复的条目。
  • 我已经添加了)。我仍然收到以下错误: src/app/Services/requirement.service.ts(185,17) 中的错误:错误 TS2345:类型参数 '(结果:{ 要求:要求;产品集:产品集;产品:产品; }[]) => void' 不能分配给类型为 '(value: { requirements: Requirement; pSet: ProductSet; product: Product[]; }[]) => void' 的参数。参数“结果”和“值”的类型不兼容。
  • 类型'{要求:要求; pSet:产品集;产品:产品[]; }[]' 不可分配给类型 '{ 要求:要求;产品集:产品集;产品:产品; }[]'。类型'{要求:要求; pSet:产品集;产品:产品[]; }' 不可分配给类型 '{ 要求:要求;产品集:产品集;产品:产品; }'。 '{ 要求:要求;类型中缺少属性 'productSet' pSet:产品集;产品:产品[]; }'。
  • 将您的 pSet 参数重命名为 productSet
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-17
  • 2012-09-10
  • 2019-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-05
相关资源
最近更新 更多