Angular / TS: асинхронный HTTP-вызов внутри цикла forEach - PullRequest
1 голос
/ 10 марта 2019

Есть ли способ заставить цикл forEach в Typescript ждать так, чтобы асинхронный код, такой как http-вызов, мог завершиться правильно.

Допустим, у меня есть три массива 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 делает непредсказуемым вызов функции loadC () после массива b []правильно загружен данными, полученными из http-вызова.Как заставить цикл forEach в loadB () ждать получения всех результатов 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).Куда я иду не так?

1 Ответ

1 голос
/ 10 марта 2019

Есть несколько способов добиться этого. Я предлагаю использовать операторы rxJs concatAll и concatMap , поэтому последовательно запускайте httpCalls. Я подготовил быстрый фрагмент. Сначала он загружает 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
}));

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...