【问题标题】:How to use forkJoin properly如何正确使用forkJoin
【发布时间】:2018-11-03 07:17:39
【问题描述】:

我对内部 Observable 有疑问。

const wordsCollection = this.db.collection('words').valueChanges();

return wordsCollection.pipe(
      map((words: Word[]) => {
        return words.map((word: Word) => {
          const categories = word.categories.map((categoryId: string) => {
            return this.db.collection('categories').doc(categoryId).valueChanges().pipe(
              map((category: Category) => {
                return category;
              })
            );
          });
          return {
            ...word,
            categories: categories
          };
        });
      })
    );

结果是这样的:

{
word: 'blabla',
categories: Observable (instead of for example 'english')
}

我知道我需要使用类似 forJoin 的东西,但不知道如何正确使用它。有什么帮助吗?

【问题讨论】:

  • 什么是word.categories?它是一个类别 ID 数组吗?你期待什么结果? {word, category} 对的数组?
  • 是的,word.categories 是一个带有类别 ID 的数组,结果应该得到类别名称而不是 ID(现在每个单词都有属性 'categories',其值类似于 ['id1', 'id2 '])
  • 那么你想要[{word: 'blabla', categories: ['english', 'italian']}, {word: 'buu', categories: ['french', 'german']}这样的东西吗?
  • 是的,正是:)

标签: javascript firebase rxjs google-cloud-firestore observable


【解决方案1】:

如果我对您的问题的理解正确,这些可能是一些解决方案的建议。

为了让事情稍微清楚一点,至少对我来说,我将开始创建一个函数,该函数期望 categoryId 作为输入并返回该类别的 Observable,即类似

function getCategory(categoryId: number) {
  return this.db.collection('categories').doc(categoryId).valueChanges();
}

然后我会像这样构建我需要的 Observable

wordsCollection.pipe(
  mergeMap(words => words),
  map(word => {
    const categoryRequests = word.categories.map(categoryId => getCategory(categoryId));
    return {word: word.word, categoryRequests};
  }),
  mergeMap(({word, categoryRequests}) => forkJoin(categoryRequests).pipe(map(categories => ({word, categories}))))
)

这里重点如下

  • 第一个mergeMapwords 数组展平并创建一个 Observable 发出 Array 的每个元素。
  • 在第二个运算符 map 中,您开始创建一个 Observables 数组,它表示从其 categoryId 开始获取 category 的请求,然后您返回一个对象,该对象同时具有 @ 的内容987654329@(我假设 Word 类型有一个属性 word 包含实际单词)和相关的类别请求
  • 然后我需要使用forkJoin 来执行请求,因此我使用mergeMap 将前一个map 运算符返回的对象转换为一个Observable,它在所有将categoryIds 转换为@987654336 的请求时发出@为某某word已完成
  • forkJoin 后面的pipe 只是用来创建具有wordcategories 属性的对象

由于我没有现成的 Firebase 环境,我使用以下代码模拟了 Firebase Observables

const words = [
  {word: 'abc', categories: [1, 2, 3]},
  {word: 'cde', categories: [3, 4, 5]},
];

const categories = {
  1: 'X',
  2: 'Y',
  3: 'Z',
  4: 'X',
  5: 'W',
}

function getCategory(categoryId: number) {
  return of(categories[categoryId])
}

【讨论】:

  • 某事不工作,因为订阅没有返回我(似乎请求正在等待,没有错误,没有数据)。我在 forkJoin 之前添加了调试器,我有这个: { word: one word data, categoryRequests: observable } 我也在最后一张地图中添加了调试器,这个调试器没有停止我的应用程序,下面的代码:mergeMap(({word, categoryRequests} ) => { debugger; return forkJoin(categoryRequests).pipe( map(categories => { debugger; return ({word, categories}) }) ); } )
  • 我没有 Firebase 环境,所以我模拟了 Firebase 生成的 Observables。我已经用模拟代码和数据更新了我的答案。
  • 这是行不通的,因为如果 forkJoin 中的多个 observable 中的任何一个失败,整个流都会失败。所以我添加了: private getCategory(categoryId: string) { return this.db.collection('categories').doc(categoryId).valueChanges().pipe(take(1));它几乎可以工作,但是现在当我订阅时,console.logs 被触发了两次:{object 1} {object 2} 我需要它的数组
  • 好的,我将 toArray() 添加到 wordsCollection 管道的末尾,它的工作:)
【解决方案2】:

试试这个:

const wordsCollection = this.db.collection('words').valueChanges();

return wordsCollection.pipe(
  mergeMap((words: Word[]) => Rx.of(...words).pipe(
    mergeMap((word: Word) => Rx.of(...word.categories).pipe(
      mergeMap(categoryId => this.db.collection('categories').doc(categoryId).valueChanges()),
      toArray(),
      map(categories => ({
        ...word,
        categories
      }))
    ))
  ))
);

【讨论】:

  • 谢谢,但有些东西不起作用,因为订阅什么也没给我(似乎请求正在等待,没有错误,没有数据)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-29
  • 1970-01-01
相关资源
最近更新 更多