RxJS:如何发出原始值,然后在完成后减少?

时间:2017-05-18 23:48:29

标签: rxjs rxjs5

我想从RxJS流中发出所有原始值,然后在完成时发出摘要。

Reduce会停止原始值的发射。扫描会发出每个总数而不是原始值。

这是我的hacky解决方案:

let total = {
  total: 0
};

Rx.Observable.range(1, 3)
  .do(val => {
    total.total += val;
  })
  .concat(Rx.Observable.of(total))
  .subscribe(
    value => {
      console.log('Next:', value)
    }
  );

// Next: 1
// Next: 2
// Next: 3
// Next: { total: 6 }

使用纯RxJS流执行此操作的简单方法是什么?

3 个答案:

答案 0 :(得分:4)

使用多播

Rx.Observable.range(1, 3)
 .multicast(new Rx.Subject(), (shared)=> {
    return Rx.Observable.merge(shared, shared.reduce((acc, x)=>acc+x,0))
 })
.subscribe(x=>console.log(x))

答案 1 :(得分:2)

作为替代方案,您可以避免使用share()并制作两个Observable链并只生成一个链:

Observable.range(1, 3)
    .concat(Observable.of(null))
    .scan((obj, curr) => {
        if (curr) {
            obj.acc.push(curr);
        }
        obj.curr = curr;
        return obj;
    }, { acc: [], curr: 0 })
    .map(obj => obj.curr === null
        ? { total: (obj.acc.reduce((acc, curr) => acc + curr, 0)) }  // count total
        : obj.curr  // just return current item
    )
    .subscribe(console.log);

这会打印出您期望的结果:

1
2
3
{ total: 6 }

即使使用share()看起来很简单,也要注意它实际上是两次订阅源Observable。在实践中,根据您将使用的Observable来源,这对您来说不是问题。

试试这个并看到每个数字都打印两次:

let source = Observable.range(1, 3).do(console.log).share();

答案 2 :(得分:1)

怎么样?

let source = Observable.range(1, 3).share();

let totalOb = source
    .reduce((total, value) => total + value, 0);

source
    .concat(totalOb)
    .subscribe( value => console.log(`Next: ${value}`) );

输出:

Next: 1
Next: 2
Next: 3
Next: 6

您可以使用throwcatch分隔数据和摘要。

let source = Observable.range(1, 3).share();

let totalOb = source
    .reduce((total, value) => total + value, 0)
    .mergeMap(total => Observable.throw(total));

source
    .concat(totalOb)
    .subscribe(
        value => console.log(`Next: ${value}`),
        value => console.log(`Total: ${value}`)
    );

输出:

Next: 1
Next: 2
Next: 3
Total: 6