我有两个observable,我想听最后发出第一个值的那个,是否有一个运算符?这样的事情:
let obs1 = Rx.Observable.timer(500,500);
let obs2 = Rx.Observable.timer(1000,1000); // I want the values from this one
let sloth = Rx.Observable.sloth(obs1,obs2);
sloth
observable将从obs2
发出值,因为它是最后一个发出第一个值的值。
如果情况并非如此,还有其他办法吗?
答案 0 :(得分:1)
我现在看到了这种可能性,但如果有人发现其他任何事情,我很好奇:
let obs1 = Rx.Observable.timer(500,500).map(i=>`cheetah ${i}`);
let obs2 = Rx.Observable.timer(1000,1000).map(i=>`sloth ${i}`);
let sloth = Rx.Observable.merge(
obs1.take(1).mapTo(obs1),
obs2.take(1).mapTo(obs2)
).takeLast(1).mergeAll()
sloth.subscribe(data=>console.log(data))

<script src="https://unpkg.com/@reactivex/rxjs@5.3.0/dist/global/Rx.js"></script>
&#13;
@ user3743222指出
编辑(非常好的昵称:-D),它不适用于热观察,但这应该没问题:
let obs1 = Rx.Observable.timer(500,500).map(i=>`cheetah ${i}`).publish();
let obs2 = Rx.Observable.timer(1000,1000).map(i=>`sloth ${i}`).publish();
obs1.connect();
obs2.connect();
let sloth = Rx.Observable.merge(
obs1.take(1).map((val)=>obs1.startWith(val)),
obs2.take(1).map((val)=>obs2.startWith(val))
).takeLast(1).mergeAll();
sloth.subscribe(data=>console.log(data));
&#13;
<script src="https://unpkg.com/@reactivex/rxjs@5.3.0/dist/global/Rx.js"></script>
&#13;
答案 1 :(得分:1)
我喜欢你的解决方案(虽然我怀疑你可能永远不会看到第一个发射值,如果你有一个热流 - 如果源是冷的,一切似乎都很好)。你可以让jsfiddle
检查一下吗?如果你不错过任何价值,你的解决方案是最好的。如果这样做,可以通过将跳过的值添加回源(obs1.take(1).map(val => obs1.startWith(val))
来更正它。
否则,对于通用冗长的解决方案,这里的关键是你有状态,所以你还需要scan
运算符。我们使用索引标记源,并且我们保持一个状态,该状态表示已经启动的源的索引。当除了一个之外的所有人都已经开始时,我们知道那个没有的索引,我们只选择那个的值。请注意,这应该独立于源是热还是冷,因为所有源都是一次通过,即,没有多个订阅。
Rx.Observable.merge(
obs1.map(val => {val, sourceId: 1})
obs2.map(val => {val, sourceId: 2})
obsn.map(val => {val, sourceId: n})
).scan(
(acc, valueStruct) => {
acc.valueStruct = valueStruct
acc.alreadyEmitted[valueStruct.sourceId - 1] = true
if (acc.alreadyEmitted.filter(Boolean).length === n - 1) {
acc.lastSourceId = 1 + acc.alreadyEmitted.findIndex(element => element === false)
}
return acc
}, {alreadyEmitted : new Array(n).fill(false), lastSourceId : 0, valueStruct: null}
)
.map (acc => acc.valueStruct.sourceId === acc.lastSourceId ? acc.valueStruct.val : null )
.filter(Boolean)
也许有更短的,我不知道。我会试着把它放在一个小提琴里,看看它是否真的有效,或者如果你做的话,请告诉我。
答案 2 :(得分:1)
这个怎么样?
let obs1 = Rx.Observable.timer(500,500);
let obs2 = Rx.Observable.timer(1000,1000);
let sloth = Rx.Observable.race(
obs1.take(1).concat(obs2),
obs2.take(1).concat(obs1)
).skip(1);
作为具有多个Observables支持的函数:
let sloth = (...observables) =>
observables.length === 1 ?
observables[0] :
observables.length === 2 ?
Rx.Observable.race(
observables[0].take(1).concat(observables[1]),
observables[1].take(1).concat(observables[0])
).skip(1) :
observables.reduce((prev, current) => sloth(prev, current))[0];
答案 3 :(得分:1)
我遇到了同样的问题,并且可以使用merge
和skipUntil
的组合来解决。如果pipe(last())
同时完成,则会使您停止接收多个结果。
尝试将以下内容粘贴到https://rxviz.com/中:
const { timer, merge } = Rx;
const { mapTo, skipUntil, last } = RxOperators;
let obs1 = timer(500).pipe(mapTo('1'));
let obs2 = timer(1000).pipe(mapTo('2')); // I want the values from this one
let sloth = merge(
obs1.pipe(skipUntil(obs2)),
obs2.pipe(skipUntil(obs1))
).pipe(last())
sloth
答案 4 :(得分:0)
使用RxJS 6和ReplaySubject:
function lastOf(...observables) {
const replayable = observables
.map(o => {
let r = o.pipe(multicast(new ReplaySubject(1)));
r.connect();
return r;
});
const racing = replayable
.map((v, i) => v.pipe(
take(1),
mapTo(i),
))
;
return of(...racing).pipe(
mergeAll(),
reduce((_, val) => val),
switchMap(i => replayable[i]),
);
}
使用:
const fast = interval(500);
const medium = interval(1000);
const slow = interval(2000);
lastOf(fast, slow, medium).subscribe(console.log);