RxJS——repeat 和 share 两个和订阅有关的操作符

我们知道 takeWhile 操作符在条件函数不满足时立即触发流的 complete 事件,所表现出来的效果就是点击任何按钮都不再有任何反应。

const addOneOrReset = (time = 1000) =>
merge(
  interval(time).pipe(
    takeUntil(pauseBtnClick$),
    mapTo(addOne)
  ),
  resetBtnClick$.pipe(mapTo(reset))
);

const time$ = merge(
  startBtnClick$.pipe(mapTo(1000)),
  halfBtnClick$.pipe(mapTo(500)),
  quarterBtnClick$.pipe(mapTo(250))
);

const timer$ = time$.pipe(
  switchMap(addOneOrReset),
  startWith({ count: 0 }),
  scan((acc, current) => current(acc)),
  map(obj => obj.count),
  tap(v => setTxt(v))
);

const subscription = timer$
.pipe(
  takeWhile(data => data <  5),
)
.subscribe(
  x => console.log(x),
  err => console.log(err),
  () => console.log("complete")
);

当我们点击开始按钮时,定时器流开始产生事件。由于 takeWhile 操作符的原因,到 5 时控制台将输出 complete。

这里先说明下界面和控制台输出的数字为什么不一样。因为界面更新是在 tap 操作符做的,要先于 takeWhile 操作符。当界面更新为 5 时,takeWhile 操作停止了事件流。

这时,我们再点击界面上的任何按钮都没有任何反应了。

现在我们可以把这个定时器想象成一个 5 秒倒计时器,那我们的需求来了,现在的倒计时器是一次性的(除非刷新页面),如何能让它在一次倒计时完成后恢复到起始状态呢?这时,repeat 操作符闪亮登场了。

下面只把变化的代码贴出来:

const subscription = timer$
.pipe(
  takeWhile(data => data <  5),
  repeat()
)
.subscribe(
  x => console.log(x),
  err => console.log(err),
  () => console.log("complete")
);

我们在 takeWhile 操作符下面添加了 repeat 操作符,这时的效果如下图:

倒计时清零的需求完成了。这时我们还发现,控制台再也没有输出 complete ,这时因为我们没有给 repeat 传入任何参数,代表无限重复执行。大家可以自己试试传入了参数是什么效果。

下面要重点说明的是,使用 repeat 操作符的位置将影响它的行为,这里交给读者自己去尝试,比如把 repeat 放在 takeWhile 的前面会是什么效果。使用 repeat 操作符的最佳实践是放在离订阅函数最近的位置。

接下来,我们先来看看下面的代码会有什么输出:

const timer$ = time$.pipe(
  switchMap(addOneOrReset),
  startWith({ count: 0 }),
  scan((acc, current) => current(acc)),
  map(obj => obj.count),
  tap(v => setTxt(v)),
);

timer$.subscribe(console.log)
timer$.subscribe(console.log)

我们订阅了两遍定时器流,控制台输出了两遍 0 ,这合情合理,每个订阅函数拥有独立的定时器流。但假如我们想让后来的订阅函数永远得到的是最新内容呢?就像订阅杂志,你得到杂志是从你订阅的时间以后的杂志,总不能把以前 20 年的杂志都给你吧?share 操作符可以帮你完成这个需求。我们只需在定时器流的最后加入 share,那么第二个 subscribe 函数将不会打印输出,这个就交给大家自己试试吧。

const timer$ = time$.pipe(
  switchMap(addOneOrReset),
  startWith({ count: 0 }),
  scan((acc, current) => current(acc)),
  map(obj => obj.count),
  tap(v => setTxt(v)),
  share(),// <--- 这里
);

如有任何问题,更多内容请关注微信公众号“读一读我”(只给你实际工作用得上的干货)。

你可能感兴趣的:(RxJS——repeat 和 share 两个和订阅有关的操作符)