Javascript RxJS CombineTest不等待源观测值发射?

Javascript RxJS CombineTest不等待源观测值发射?,javascript,rxjs,Javascript,Rxjs,我有两个源观测值,当一个源观测值发出时,我需要计算一些数据。我试图使用combineAll()操作符,但它只在每个源观测值第一次发出时发出一个值 是否有类似于combineAll()的操作符,在任何源观测值第一次发射时立即发射?如果没有,最清晰的方法是什么 我所尝试的: const source1$=service.getSomeData(); const source2$=service.getOtherData(); 组合测试( 资料来源1$, 资料来源2$ ).烟斗( 映射([source

我有两个源观测值,当一个源观测值发出时,我需要计算一些数据。我试图使用
combineAll()
操作符,但它只在每个源观测值第一次发出时发出一个值

是否有类似于
combineAll()
的操作符,在任何源观测值第一次发射时立即发射?如果没有,最清晰的方法是什么

我所尝试的:

const source1$=service.getSomeData();
const source2$=service.getOtherData();
组合测试(
资料来源1$,
资料来源2$
).烟斗(
映射([source1Data,source2Data]=>{
//这段代码只有在两个可观察对象第一次发射时才执行
返回source1Data+source2Data;
})
)

一种方法是在所有源代码前面加上
startWith

combineLatest([
  source1$.pipe(startWith(?)),
  source2$.pipe(startWith(?)),
])
当任何源观测物第一次发射时,它就会发射吗


看起来您可能正在寻找
race(source1$,source2$)
可观察的创建方法,或者可能只是
merge(source1$,source2$).pipe(take(1))
。但这实际上取决于你想做什么。

如果我理解正确,你想要一个如下图所示的模式:

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

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

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

首先是输入流,为了这个示例,我将它们设置为主题,这样我们就可以手动推送数据:

const stream1$ = new Subject();
const stream2$ = new Subject();
接下来,我们将组合输入,首先通过startWith操作符进行管道传输。这确保了CombineTest生成一个可立即发射的可观测值——准确地说是
[null,null]

const combined$ = combineLatest(
  stream1$.pipe(startWith(null)),
  stream2$.pipe(startWith(null)),
);
现在您有了一个observable,它总是发出长度为2的数组,包含您的数据(本例中的数字)和null的任意组合,如下图所示:

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

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

combined$                     [NULL, NULL] --- [1, NULL] --- [12, NULL] --- [12, 30] -------
最后,您可以检查并
将该输出映射为所需格式:两个数字的总和(如果两个数字都可用),或第一个可用值:

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 -------------
还有一个问题:从
组合$
发出的第一个值是
[null,null]
,导致
处理组合$
最初发出
null
。解决此问题的一种方法是使用
skipWhile
将另一个管道连接到
processedcompositions$

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 -------------
另一种更好的方法是在创建
处理组合$
(现在实际上是
最终$
)之前过滤
组合$
流:

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
使用的进口:


例如,我试图返回
source1Data+source2Data
,因此我需要
map()
回调中的每个源数据(因此只有
merge()
race()
不合适)。我没有想过使用
startWith()
运算符,但由于数据来自应用程序状态(ngrx存储),我不想“猜测”它的第一个值。@EliasGarcia您想要哪一个?仅当所有源可观测物发射时发射或至少一个源发射时发射?我需要发射第一个可观测物的值,直到第二个到达。当第二个到达时,我需要进行一些计算,并根据两个可观察的输入返回一个组合结果。因此,可能是这样的
concat(source1$.pipe(take(1)),combinelateest(source1$,source2$)
这正是我要找的!没有过滤器,地图,这是最干净的方法,非常感谢!您的示例表明您需要两个值来执行该操作。仅使用1个值(第一个要发射的值)将做什么?是的,我需要两个值来执行操作。但是,在获得这两个值之前,CombineTest发出的值是
null
。我想要的是发出第一个可观测值的起始值(例如空数组),直到第二个到达。然后,我可以执行该操作,并根据第二个可观察对象返回另一个列表。我不确定我是否理解您试图实现的目标
CombineTest
在所有输入观测值至少发出一次之前不会发出任何东西。如果您希望在不考虑输入状态的情况下立即接收复合值(例如,
[null,'source2Data']
),除了通过每个可观察的输入显式推送初始值
null
之外,没有其他方法。我猜您需要使用LatestFrom,而不是CombineTest。通过这种方式,你可以有一个可观测值来确定什么时候应该发生什么,而LatestFrom只会给你一些其他可观测值的最后一个发射值。您可以将多个观测值传递到withLatestFrom,它将为您提供一个值数组->每个观测值一个值。你可能仍然需要startWith。这么好的答案,深思熟虑的,有用的图表,喜欢它!我最后使用了
combinelatetest
(但是
startWith(null)
只在
2nd
上,不会立即启动),然后
.pipe
然后
map()
其中
if(2nd==null)返回1st
。。。当然,它没有你的好/正确,但它很短,适合我的情况:)再次感谢!