【问题标题】:RXJS: Combining multiple calls from foreach into a single observableRXJS:将来自 foreach 的多个调用组合成一个 observable
【发布时间】:2020-06-19 05:11:46
【问题描述】:

一段时间以来,我一直在尝试组合多个异步调用,每次接近时,我都会陷入我试图解决的 foreach 循环中。

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

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

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

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

这与我在my previous question 上的推荐非常接近。我想将此添加到我原来的问题中,但我觉得这不是正确的做法,因为我不再为嵌套的可观察对象而苦苦挣扎,而是在循环它们。

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

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

    return merge(...ques);
  }

【问题讨论】:

    标签: angular loops foreach rxjs observable


    【解决方案1】:

    首先,我喜欢把它放到一个专用函数中的想法,这样你就可以将接口与实现分开。

    你要解决的问题应该在管道中是可行的,这只是一个弄清楚发生了什么的案例。

    • 您从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), []);
    }
    

    【讨论】:

    • flat() 是较新版本的打字稿的一部分吗?它似乎没有得到认可
    • flat() 是一个数组函数。这可能意味着questions 不是一个实际的数组。或者您使用的是旧浏览器。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 您可以使用reduce 代替flat。我会更新我的答案
    • 它将问题识别为 Question[][]。根据您发布的链接,它似乎是新版本 ES 的一部分,我会尝试更新它以查看是否可以修复它。
    • 我已将答案修改为使用reduce。如果仍然无法识别,则可能是 questions 不是实际数组
    • 当然,它不需要在自己的函数中。这里的含义是您可以将其公开为一个通用函数。或者,如果您愿意,您可以随时包含 flat polyfill。
    【解决方案2】:

    您应该将这些流与forkJoincombineLatest 结合起来:

    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
    }
    

    【讨论】:

    • 当我尝试这样做时,我得到一个 Type 'Observable' 不可分配给类型 'Observable'。 例外。
    • 这不是异常,是类型错误。您可以将结果映射到您想要的任何内容。试试pipe(map(questions =&gt; questions.flat()))
    猜你喜欢
    • 1970-01-01
    • 2018-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多