RXJS:将来自 foreach 的多个调用组合成单个可观察值

iCV*_*iCV 8 foreach loops observable rxjs angular

我已经尝试组合多个异步调用有一段时间了,每次接近时,我都会陷入我试图解决的 foreach 循环。

目前,我将一个类别数组传递给一个函数,该函数将返回一个包含所有问题(所有类别组合)的数组的可观察值。
我在想

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

Run Code Online (Sandbox Code Playgroud)

然后我就可以像这样使用它:

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

Run Code Online (Sandbox Code Playgroud)

这是我能得到的最接近某人对我之前的问题的建议。我想将其添加到我原来的问题中,但我觉得这不是正确的做法,因为我不再为嵌套的可观察量而苦苦挣扎,而是在循环它们。

编辑
尝试了其他方法,但我怀疑这是正确的方法

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

    return merge(...ques);
  }
Run Code Online (Sandbox Code Playgroud)

Kur*_*ton 6

首先,我喜欢将其放入专用函数中的想法,以便可以将接口与实现分开。

你想要解决的问题应该在管道中是可行的,这只是解决正在发生的事情的情况。

  • 您从一系列Category.
  • 对于每个类别,您想要调用一个服务函数,该函数返回一个可观察的Question.
  • 您想要从函数异步返回扁平化的问题数组

我会通过以下方式解决这个问题:

  • 创建可观察数组。这可以帮助您了解您要从什么开始
  • forkJoin可观察的数组。本身这将返回一个二维数组
  • map二维数组转换为平面数组
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), []);
}
Run Code Online (Sandbox Code Playgroud)