响应式编程实战—— RxJS 暂停事件流与初始值

2020-06-01  本文已影响0人  du1dume

昨天我们讲到开始和停止一个事件流,回顾一下代码:

const stopBtnClick$ = fromEvent(stopBtnRef.current, "click");
const startBtnClick$ = fromEvent(startBtnRef.current, "click");
const perSecond$ = interval(1000);

const intervalCanBeStopped$ = perSecond$
.pipe(takeUntil(stopBtnClick$));

const subscription = startBtnClick$.pipe(
  switchMapTo(intervalCanBeStopped$)
).subscribe(v => console.log(v));

现在的情况是,当我们点击停止按钮后再次点击开始按钮,计数又是从 0 开始。如果我们想从停止时的数字继续计数呢?按照常规的编程模型,我们会这样做:

let count = 0;
const subscription = startBtnClick$
.pipe(
  switchMapTo(intervalCanBeStopped$),
)
.subscribe(v => console.log(count++));

这样做,确实可以实现从暂定点开始恢复计数。但:

不要这样做!!!

不要这样做!!!

不要这样做!!!

在 Rx 编程模型中,正确的做法是使用 scan 操作符。scan 操作符就像是数组的 reduce 函数。它的使用方法是:

scan((acc, value,index) => acc, seed)

scan 的参数有两个,一个是累积函数,是必须提供的;另一个是起始值,为可选参数。累积函数中的 acc 参数是累积值,value 是原始流的值,index 是索引,返回值是累计值。返回值会作为下次函数运行的参数。

具体使用方法如下:

startBtnClick$
.pipe(
  switchMapTo(intervalCanBeStopped$),
  scan((acc)=>{count: acc.count + 1}, {count:0})
)

这里我们没有使用原始流中值,我们的累计值的初始值是个对象,对象里面有个 count 属性,值为 0 : {count:0},也就是对应函数参数中的 acc。累积函数的返回值为累计值中的 count 属性 +1。这样就完成了暂停功能。

下面我们来看看另一种为 scan 提供初始值的方法。

startWith:这个操作符的作用是在原始流产生值之前插入提供给它参数值。

我们的代码改动如下:

startBtnClick$
.pipe(
  switchMapTo(intervalCanBeStopped$),
  startWith({count: 0}),
  scan((acc)=>{count: acc.count + 1})
)

startWith 就像是加塞儿,intervalCanbeStopped$ 产生了第一个值,碰到 startWith 后,startWith 中的参数将加塞到这值之前传递给 scan 。我们知道 scan 操作符在不提供第二个参数时,会使用原始流传递过来的第一个值作为初始值。因此,startWith 中的参数成为了 scan 的初始值。

今天使用到的两个操作符将在前端中的状态管理,网络请求中经常使用到。今后的文章中会提到。

如有任何问题,请关注公众号“读一读我”。

上一篇下一篇

猜你喜欢

热点阅读