是否与RxJS中的`race`运算符相反?

时间:2017-04-24 15:14:30

标签: javascript rxjs rxjs5

我有两个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发出值,因为它是最后一个发出第一个值的值。

如果情况并非如此,还有其他办法吗?

5 个答案:

答案 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;
&#13;
&#13;

@ user3743222指出

编辑(非常好的昵称:-D),它不适用于热观察,但这应该没问题:

&#13;
&#13;
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;
&#13;
&#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)

我遇到了同样的问题,并且可以使用mergeskipUntil的组合来解决。如果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);