RX JS: объединение нескольких вызовов из foreach в одну наблюдаемую - PullRequest
0 голосов
/ 06 марта 2020

Я уже некоторое время пытаюсь объединить несколько асинхронных c вызовов, и каждый раз, когда я сближаюсь, я застреваю на foreach l oop Я пытаюсь решить.

В данный момент я передаю массив категорий в функцию, которая возвращает наблюдаемую информацию, содержащую массив всех вопросов (всех категорий вместе взятых).
Я думал о

  getQuestions(categories: Category[]): Observable<Question[]> {
    let q: Question[];
    categories.forEach(c => {
      this.cs
        .getQuestionsCategory(c.id)
        .pipe(map(questions => q.push(...questions)));
         //return q somehow?
    });
  }

I тогда я смог бы использовать его так:

   let result: Result;
    let res: [Category[], Account, Answer[]];

    return this.getResultByRouteParamId(route).pipe(
      tap(resu => (result = resu)),
      switchMap((result: Result) =>
        this.forkJoinQuizCategoriesAccountAnswers(result)
      ),
      tap(results => (res = results)),
      switchMap(x => this.getQuestions(res[0])),
      map(questions => {
        // Create objects of questions, answers and category and add them to a QuestionAnswer object
        // Return new UserResult containing the user and a list of QuestionAnswer objects
        return new UserResult(null, null);
      })
    );

Это настолько близко, насколько я мог бы получить к чьей-либо рекомендации по моему предыдущему вопросу . Я хотел добавить это к моему первоначальному вопросу, но я чувствую, что это будет неправильно, поскольку я больше не борюсь с вложенными наблюдаемыми объектами, а скорее с их циклом.

РЕДАКТИРОВАТЬ
Пробовал что-то еще, но я сомневаюсь, что это правильный способ сделать это

  getQuestions(categories: Category[]): Observable<Question[]> {
    let ques = categories.map(c =>
      this.cs.getQuestionsCategory(this.auth.token, c.id)
    );

    return merge(...ques);
  }

Ответы [ 2 ]

1 голос
/ 06 марта 2020

Во-первых, мне нравится идея поместить это в отдельную функцию, чтобы вы могли отделить интерфейс от реализации.

Проблема, которую вы хотите решить, должна быть выполнима в конвейере, это просто случай выяснения того, что происходит.

  • Вы начинаете с массива Category.
  • Для каждой категории вы хотите вызвать сервисную функцию, которая возвращает наблюдаемую массив Question.
  • Вы хотите вернуть этот плоский массив вопросов из функции асинхронно

Я бы подошел к этому следующим образом:

  • Создание наблюдаемый массив. Это поможет вам понять, что вы начинаете с
  • forkJoin наблюдаемого массива. Само по себе это вернет 2-мерный массив
  • map 2-мерный массив в плоский массив
getQuestions(categories: Category[]): Observable<Question[]> {
  // firstly, start out with an array of observable arrays
  const observables: Observable<Question[]>[] = categories.map(cat => 
    this.cs.getQuestionsCategory(cat.id));

  // run all observables in parallel with forkJoin
  return forkJoin(
    observables
  ).pipe(
    // now map the array of arrays to a flattened array
    map(questions => this.flat(questions))
  );
}

// alternative to arr.flat()
private flat<T>(arr: T[][]): T[] {
  return arr.reduce((acc, val) => acc.concat(val), []);
}
0 голосов
/ 06 марта 2020

Вы должны объединить эти потоки с forkJoin или combineLatest:

getQuestions(categories: Category[]): Observable<Question[]> {
    return forkJoin(
        categories.map(category => this.cd.getQuestionsCategory(category.id))
    ).pipe(tap(questions => { console.log(questions) }));  // Here you can change the data structure of questions instead of just logging them
}
...