使用RxJS为点击事件流设最小执行间隔,解决Firebase数据覆盖问题
我来帮你解决这个问题!
问题根源分析
你当前的代码用flatMap处理每个buffer对应的流,这会导致不同buffer的流并行执行。比如第一个buffer的concatMap流和第三个buffer的concatMap流会同时运行:第一个流里的编号2是从第一个buffer处理时间开始延迟2秒,第三个流里的编号11是从第三个buffer处理时间开始延迟2秒,两个buffer的处理时间间隔1秒,最终刚好凑到同一时间触发,这就是你看到的冲突问题。
解决方案
我们需要把所有点击事件(不管属于哪个buffer)放到统一的执行队列里,确保每个操作严格按顺序、间隔至少2秒执行。这里提供两种适配不同需求的方案:
方案1:直接对每个点击事件排队(无需buffer分组)
如果不需要保留每秒分组的日志,只是要保证操作间隔2秒,最简单的方式是去掉bufferTime,直接用concatMap处理每个点击:
(function($){ $(function($, undefined){ var count = 0; const click$ = Rx.Observable.fromEvent($('.btn'), 'click').share(); click$ .map((ev)=> { count ++; return count; }) .concatMap(num => Rx.Observable.of(num).delay(2000)) // 每个操作延迟2秒,且严格依次执行 .subscribe((a)=> { console.log(`${a} : ${new Date()}`) }); }); })(jQuery);
方案2:保留buffer分组,同时统一执行队列
如果你需要保留bufferTime来记录每秒的点击分组日志(比如new buffer: xxx),可以把所有buffer的元素展开成单一序列,再用concatMap统一排队:
(function($){ $(function($, undefined){ var count = 0; const click$ = Rx.Observable.fromEvent($('.btn'), 'click').share(); click$ .map((ev)=> { count ++; return count; }) .bufferTime(1000) .filter(buffer => buffer.length > 0) .do((buffer) => console.log(`new buffer: ${buffer}`)) .mergeAll() // 将每个buffer的数组元素展开为连续的单元素流 .concatMap(num => Rx.Observable.of(num).delay(2000)) // 所有元素统一排队,间隔2秒执行 .subscribe((a)=> { console.log(`${a} : ${new Date()}`) }); }); })(jQuery);
为什么这样有效?
concatMap的核心特性是等待前一个内部流完成后,再订阅下一个内部流。每个Rx.Observable.of(num).delay(2000)会在2秒后发出num并完成,因此下一个元素会在前一个元素执行完2秒后才触发。- 方案2中的
mergeAll把每个buffer的数组元素展开成连续流,让所有点击事件不管属于哪个buffer,都进入同一个执行队列,彻底避免了并行执行的冲突。
测试后你会发现,所有操作的执行时间会严格间隔2秒,再也不会出现两个操作同时触发的情况。
内容的提问来源于stack exchange,提问作者user8987378
相关产品推荐
相关产品推荐

