如何实现保留首个触发值的debounceTime,同时检测事件流的起止状态
实现方案
你需要的是可以同时输出「事件序列首触发信号」和「事件静默后结束信号」的逻辑,以下提供两种常用实现方式:
方案1:基于RxJS实现
通过多播源事件流分别派生开始、结束两个信号流,合并后即可得到符合要求的输出:
import { fromEvent, merge } from 'rxjs'; import { map, debounceTime, share, take, repeat } from 'rxjs/operators'; // 以键盘按下事件为例 const source$ = fromEvent(document, 'keydown').pipe(share()); const SILENT_DURATION = 50; // 开始信号S:每次事件序列首次触发时输出,后续同批次事件忽略 const start$ = source$.pipe( take(1), map(val => ({ type: 'start', value: val })), repeat() ); // 结束信号E:最后一次事件触发后静默指定时长输出 const end$ = source$.pipe( debounceTime(SILENT_DURATION), map(val => ({ type: 'end', lastValue: val })) ); // 合并得到最终流 const result$ = merge(start$, end$); // 测试订阅 result$.subscribe(event => { if (event.type === 'start') { console.log('S:开始事件触发', event.value); } else { console.log('E:结束事件触发', event.lastValue); } });
也可以封装为通用自定义操作符复用:
function emitStartAndEnd<T>(silentTime: number) { return (source$: Observable<T>) => { const shared$ = source$.pipe(share()); const start$ = shared$.pipe(take(1), map(val => ({ type: 'start', value: val })), repeat()); const end$ = shared$.pipe(debounceTime(silentTime), map(val => ({ type: 'end', lastValue: val }))); return merge(start$, end$); }; } // 使用示例 const event$ = fromEvent(document, 'click').pipe(emitStartAndEnd(50));
方案2:原生JavaScript实现
通过标记位和定时器即可实现轻量版本:
let endTimer = null; let hasFiredStart = false; const SILENT_DURATION = 50; // 以键盘事件为例 document.addEventListener('keydown', (e) => { // 同批次事件仅触发一次开始信号 if (!hasFiredStart) { console.log('S:开始事件触发', e); hasFiredStart = true; } // 每次事件触发都重置结束定时器 clearTimeout(endTimer); }); document.addEventListener('keyup', () => { // 静默指定时长后触发结束信号 endTimer = setTimeout(() => { console.log('E:结束事件触发'); hasFiredStart = false; }, SILENT_DURATION); });
内容的提问来源于stack exchange,提问作者Evgeny Naumov
相关产品推荐
相关产品推荐

