获取并合并可观察的响应数组

时间:2018-08-20 22:08:24

标签: typescript rxjs

对于可观察数组中的每个值,获取一个数字数组,然后将所有结果合并为一个可观察数字数组。

我尝试了flatMapconcatMap的各种组合,但无法正确完成。

someFunction(): Observable<number[]> {
  // Observable<string[]>
  const strings = of(['a', 'b', 'c']);

  let index = 0;

  // Observable<number[]>
  const numbers = strings.pipe(
    flatMap(ids => ids.map(() => of([index++, index++, index++])))
  );

  return numbers; // [1, 2, 3, 4, 5, 6, 7, 8, 9]
}

1 个答案:

答案 0 :(得分:0)

看看您的代码,似乎bars发出了Bar[]类型的实例,这是由于Observable fooIds发出了ID数组的结果。

如果是这种情况,那么这里可能要注意两件事。

第一个是使用switchMap。使用此运算符,您可以在发生源可观测到的另一发射后立即完成所有正在运行的可观测。

换句话说,如果fooIds以非常快的顺序发出2个ID数组,或者与此同步,则bars到第二个ID到达时还没有完成对第一个ID数组的处理,那么第一个将被忽略,仅第二个会被处理。如果要保留所有结果,则可能要考虑使用mergeMap(又称flatMap)而不是switchMap

第二件事是使用concatAll。使用此运算符,您想要保留可观察到的源序列。产生的Observable将始终发出类型为bar[]的单个实例,其中包含使用通过bars的单个通知传递的ID检索到的所有fooIds

如果您要获得的结果是Bar的单个数组,其中包含来自fooIds的所有通知的所有项目,则可以考虑使用reduce运算符,例如通过以下方式

reduce((acc, one: any) => {
    acc = acc.concat(one);
    return acc;
}, []),

在@hdk发表评论后更新了答案

这应该是使您的示例工作正常的代码

function someFunction(): Observable<number[]> {
    // Observable<string[]>
    const strings = of(['a', 'b', 'c']);

    let index = 0;

    // Observable<number[]>
    const numbers = strings.pipe(
      mergeMap(ids => ids.map(() => of([index++, index++, index++]))),
      mergeMap(d => d)  // flattens the Observable<Observable<number>> to Observable<number>
    );

    const numbersReduced = numbers
    .pipe(
        reduce((acc: Array<number>, one: Array<number>) => {
            acc = acc.concat(one);
            return acc;
        }, new Array<number>()),
    ); 

    return numbersReduced// [1, 2, 3, 4, 5, 6, 7, 8, 9]
}

someFunction().subscribe(console.log)

关键是要认识到

mergeMap(ids => ids.map(() => of([index++, index++, index++])))

返回一个Observable<Observable<number>>

为什么?因为ids.map(....)返回了Array,即ObservableInputmergeMap需要一个返回ObservableInput的函数作为输入,因此返回Array的函数就足够了。如果在这种情况下,Array自身包含其他Observable,那么您将得到我们正在目睹的Observable<Observable<number>>返回类型。

其余部分应该或多或少简单明了。

在现实世界中,例如如果您正在处理http请求,则可以使用其他策略,例如使用forkJoin。在这种情况下,代码就是这样

function someFunction(): Observable<number[]> {
    // Observable<string[]>
    const strings = of(['a', 'b', 'c']);

    let index = 0;

    const numbers = strings.pipe(
        mergeMap(ids => {
            const numberObservables = ids.map(() => of([index++, index++, index++]));
            return forkJoin(numberObservables)
        })
    );

    const numbersReduced = numbers
    .pipe(
        map(arrayOfArrayOfNumbers => arrayOfArrayOfNumbers
            .reduce(
                (acc: Array<number>, one: Array<number>) => {
                    acc = acc.concat(one);
                    return acc;
                }, new Array<number>()
            ))
    )

    return numbersReduced// [1, 2, 3, 4, 5, 6, 7, 8, 9]
}

请注意,您仍在使用reduce,但是这次是Array reduce方法,而不是我们使用的Observable reduce方法在以前的情况下。