Angular 6: выполните 2 вызова API для каждого элемента массива

#angular #rxjs #ngrx

#angular #rxjs #ngrx

Вопрос:

В моем приложении Angular6 у меня есть эффект, который сначала получает идентификаторы пользователей, а затем выполняет 2 вызова API для каждого идентификатора: getName и getAge. В идеале я хотел бы, чтобы полученные данные были сгруппированы по идентификаторам: сначала данные для первого идентификатора, затем для второго и так далее. Пример: [ [Анна, 18], [Макс, 22] ].

Как этого добиться? Я думал, что zip-оператор может помочь, но не может найти правильный способ. С этим кодом ниже вызовы API не выполняются, хотя точка останова попадает в обе функции, которые выполняют вызовы.

 @Effect()
loadAllNames$: Observable<Action> = this.actions$.ofType(allUsersActions.LOAD_ALL_NAMES)
    .pipe(
        concatMap(action => {
            return this.apiService.getUserIds()
        }),
        switchMap(ids => {  // [ 3423, 1222, 43234, 1232];
            let observableBatch = ids.map(id => {
              this.apiService.getName(id),  // brekpoint hits this fn
              this.apiService.getAge(id) // brekpoint hits this fn
            })
            return zip(observableBatch)
            .pipe(
                switchMap((res) => {
                    this.store.dispatch(new allUsersActions.SetAllUsers(res));
                    return of(
                        new spinnerActions.HideSpinner()
                    )
                }),
                catchError(error => of(new spinnerActions.HideSpinner()))
          )
        })
    )
  

Служба API:

   public getName(id): Observable<any> {
    return this.http.get<any[]>(URL   id)
  }
  

Комментарии:

1. Каково содержимое console.log(observableBatch)? У вас есть this.apiService.getAge, также определенный рядом с getName в вашем сервисе, верно?

2. @Adam , я не знаю почему, но console.log(observableBatch) возвращает массив undefined . Однако функции в службе api получают правильные идентификаторы. И да, getAge() также существует в службе.

Ответ №1:

Это просто внутренний, switchMap который будет принимать один id за другим и запускать оба вызова одновременно.

 ...
switchMap(ids => from(ids).pipe(
  concatMap(id => forkJoin(
    this.apiService.getName(id),
    this.apiService.getAge(id),
  )),
  toArray(),
)),
  

Живая демонстрация:https://stackblitz.com/edit/rxjs-85y3bv

Комментарии:

1. эти запросы будут последовательными при такой реализации. лучше сделать их параллельными

2. @Andrey что вы подразумеваете под «эти запросы будут последовательными»? И каков правильный способ, по вашему мнению? Спасибо

3. concatMap заставляет ваши запросы отправляться один за другим, а не параллельно. Я бы сказал, что правильным способом было бы of(ids).pipe(switchMap((ids) => forkJoin(ids.map(id => forkJoin(this.apiService.getName(id),this.apiService.getAge(id))))))