RxJ合并最新,而无需等待源观测站发出?

时间:2019-01-04 12:57:24

标签: javascript rxjs

我有两个源可观测值,只要有一个源可观测值发出,我就需要从那里计算一些数据。我正在尝试使用combineAll()运算符,但它仅在每个源可观察对象首次发射时才发射一个值。

是否有任何类似于combineAll()的运算符在任何源可观察对象首次发出时发出?如果没有,最清晰的方法是什么?

我尝试过的事情:

const source1$ = service.getSomeData();
const source2$ = service.getOtherData();

combineLatest(
  source1$,
  source2$
).pipe(
  map([source1Data, source2Data] => {
    // this code only gets executed when both observables emits for the first time
    return source1Data + source2Data;
  })
)

2 个答案:

答案 0 :(得分:8)

一种方法是在所有来源前面加上this.props.dateFrom

startWith
  

任何可观察到的信号源首次发射时发出的信号?

这似乎是您在寻找combineLatest( source1$.pipe(startWith(?)), source2$.pipe(startWith(?)), ) 可以观察到的创建方法,或者仅仅是race(source1$, source2$)。但这实际上取决于您想做什么。

答案 1 :(得分:1)

如果我理解正确,则需要一种如下图所示的模式:

stream1$ => ------ 1 ------ 12 -----------------------
stream2$ => ------------------------- 30 -------------

result$  => ------ 1 ------ 12 ------ 42 --------------

如果有一个值,则发出该值。如果两者都可用,则发出两者的组合,在这种情况下为简单的总和(12 + 30 = 42);

首先输入流,为了这个示例,我将它们作为主题,因此我们可以手动推送数据:

const stream1$ = new Subject();
const stream2$ = new Subject();

接下来,我们将合并输入,首先通过startWith运算符通过管道传递。这样可以确保CombineLatest产生可观察到的并立即发出-[null, null]

const combined$ = combineLatest(
  stream1$.pipe(startWith(null)),
  stream2$.pipe(startWith(null)),
);

现在,您有一个可观察的对象,该对象总是发出长度为2的数组,其中包含数据的任何组合(在此示例中为数字)和null,如下图所示:

stream1$ | startWith(NULL) => NULL ----------- 1 ----------- 12 ----------------------------
stream2$ | startWith(NULL) => NULL ---------------------------------------- 30 -------------

combined$                     [NULL, NULL] --- [1, NULL] --- [12, NULL] --- [12, 30] -------

最后,您可以检查此输出并将其map转换为所需的格式:两个数字之和(如果两个数字均可用)或第一个数字可用:

const processedCombinations$ = combined$.pipe(
  map(([data1, data2]) => {
    if (data1 === null) return data2;
    if (data2 === null) return data1;

    return data1 + data2;
  }),
);

结果:

combined$                  => [NULL, NULL] --- [1, NULL] --- [12, NULL] --- [12, 30] -------
processedCombinations$     => NULL ----------- 1 ----------- 12 ----------- 42 -------------

一个问题仍然存在:从combined$发出的第一个值是[null, null],导致processedCombinations$最初发出null。解决此问题的一种方法是使用skipWhile将另一个管道链接到processedCombinations$上:

const final$ = processedCombinations$.pipe(skipWhile((input) => input === null));

结果:

combined$                  => [NULL, NULL] --- [1, NULL] --- [12, NULL] --- [12, 30] -------
processedCombinations$     => NULL ----------- 1 ----------- 12 ----------- 42 -------------
final$                     => ---------------- 1 ----------- 12 ----------- 42 -------------

另一种-更好的imo-方法是在根据其创建combined$(现在实际上是processedCombinations$)之前过滤final$流:

const combinedFiltered$ = combined$.pipe(
    filter(([first, second])=> first !== null || second !== null),
);

const final$ = combinedFiltered$.pipe(
    map(([data1, data2]) => {
        if (data1 === null) return data2;
        if (data2 === null) return data1;

        return data1 + data2;
    }),
);

相应的图表很好地显示了如何在流层次结构中尽早消除不相关的值:

combined$                  => [NULL, NULL] --- [1, NULL] --- [12, NULL] --- [12, 30] -------
combinedFiltered$          => ---------------- [1, NULL] --- [12, NULL] --- [12, 30] -------
final$                     => ---------------- 1 ----------- 12 ----------- 42 -------------

可以使用以下代码生成以上图表:

final$.subscribe(console.log);

stream1$.next(1);
// logs: 1

stream1$.next(12);
// logs: 12

stream2$.next(30);
// logs: 42

使用的进口:

import { combineLatest, Subject } from 'rxjs';
import { filter, map, skipWhile, startWith } from 'rxjs/operators';