如何合并多个Observable类型数组Observable <t []>?

时间:2019-05-23 09:48:02

标签: angular typescript rxjs google-cloud-firestore

我正在Firebase Firestore上运行多个查询。它们都可以返回一个Observable。我想将所有Observable合并为一个Observable。

我尝试了不同的RxJS运算符,例如; CombineLatest,forkJoin,concat,zip,merge和mergeMap。但是我无法达到预期的效果。

getPrivateBooksFromAuthors(authors): Observable<Book[]> {
    let bookRefs: Observable<Book[]>[] = [];
    authors.map(key => {
      bookRefs.push(this.afs.collection<Book>('books', ref => 
        ref.where('public', '==', false)
           .where('authors', 'array-contains', key)
        ).valueChanges())
    });
    return bookRefs[0]
}

在上面的代码中,我从authors [0]中获得了所有私人书籍。当我返回concat(... bookRefs)或concat(bookRefs [0],bookRefs [1])时,我仍然只从作者那里获得书籍[0]。我希望从提供的所有作者那里获得所有书籍。

3 个答案:

答案 0 :(得分:1)

我认为最好的解决方案是使用fork join

Fork使您可以并行执行调用。每次调用的结果都放在一个对象(join)中,您可以得到全部调用的结果。

答案 1 :(得分:0)

正如@Doflamingo所说,您可以使用 forkJoin 来并行调用它们并接收包含已解决所有响应的数组的响应。 您遇到的问题是每个响应都是Book [],因此在forkJoin中,您将收到一个Book []数组(Book [] [])。您需要将图书[] [​​] 平整到图书[]中。您可以使用地图和缩小功能

在这里我贴了一个简单的代码,该代码在应用reduce函数后控制台记录了forkJoin的响应和结果。

function mockBooks$(key): Observable<Book[]> {
  return rxjs.of([key + '-book1', key + '-book2', key + '-book3']);
}

function getPrivateBooksFromAuthors(authors): Observable<Book[]> {
    let bookRefs: Observable<Book[]>[] = authors.map(key => mockBooks$(key));

    return rxjs.forkJoin(bookRefs).pipe(
        rxjs.operators.tap((books) => console.log('After forkJoin', books)),
        // You need this flattening operation!!
        rxjs.operators.map(books => books.reduce((acc, cur) => [...acc, ...cur], []) ));
}


const authors = ['a1', 'a2', 'a3', 'a4', 'a5'];
getPrivateBooksFromAuthors(authors).subscribe((data) => console.log('Final result', data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.js"></script>

希望这会有所帮助!

答案 2 :(得分:0)

@Doflamingo向我们指出了forkJoin的方向,@Llorenç在地图上给出了一个很好的代码示例,并简化了将Observable组合成一个Observable的管道。对我来说,如果某些作者没有书籍,forkJoin就会失败。因此,我最终选择了CombineLatest(它不等待所有Observable都完成)。这是我的最终代码(所有功劳归@Llorenç):

  mockBooks$(key): Observable<Book[]> {
    return this.afs.collection<Book>('books', ref => 
      ref.where('private', '==', true)
         .where('authors', 'array-contains', key)
      ).valueChanges()
    // return of([key + '-book1', key + '-book2', key + '-book3']);
  }

  getPrivateBooksFromAuthors(authors): Observable<Book[]> {
    let bookRefs: Observable<Book[]>[] = authors.map(key => this.mockBooks$(key));

     // return combineLatest(bookRefs).pipe(
     //     tap((books) => console.log('After forkJoin', books)),
     //     // You need this flattening operation!!
     //     map(books => books.reduce((acc, cur) => [...acc, ...cur], []) ));

     return combineLatest<Book[]>(bookRefs).pipe(
        map(arr => arr.reduce((acc, cur) => acc.concat(cur) ) ),
     )
  }

我已注释掉Llorenç的代码,以显示差异。谢谢你们两个!