如何基于Promise实现可取消的timeout,且支持返回Promise供后续调用
优化实现方案
你当前的核心逻辑是实现带防抖能力的可取消超时Promise,原有实现功能是完备的,可通过封装简化调用逻辑,消除冗余的嵌套Promise:
简化自封装版本
const debounceTimeout = (ms) => { let timeoutId; let resolveFn; let pending = true; // 直接返回Promise实例,挂载额外属性和方法 const promise = new Promise(resolve => { resolveFn = resolve; timeoutId = setTimeout(() => { pending = false; resolve(); }, ms); }); promise.abort = () => { clearTimeout(timeoutId); pending = false; // 若需要在取消时触发 reject 可放开下一行注释,否则静默完成即可 // resolveFn(Promise.reject(new Error('Timeout aborted'))) }; promise.isPending = () => pending; return promise; };
适配你的业务场景的调用方式:
let timedIns; const do_timed_thing = async (v) => { // 取消上一个未完成的超时 timedIns?.isPending() && timedIns.abort(); // 新建超时实例 timedIns = debounceTimeout(1000); // 等待超时触发 await timedIns; // 执行后续逻辑 timedIns = null; add_li('timed val “' + v + '”'); };
这个版本的优势:
- 返回值本身就是Promise,无需额外访问
.wait属性,可直接用await/.then调用 - 状态和方法完全内聚,外部无需感知定时器实现细节
- 可灵活配置取消时的行为,不需要取消回调的场景下不会产生未捕获异常
AbortController 标准实现版本
如果需要和fetch等原生支持取消的API联动,可使用标准的AbortController实现,写法也足够简洁:
const debounceTimeout = (ms, signal) => { return new Promise((resolve, reject) => { const timeoutId = setTimeout(resolve, ms); signal?.addEventListener('abort', () => { clearTimeout(timeoutId); reject(new Error('Aborted')); }, { once: true }); }); };
调用方式:
let abortController; const do_timed_thing = async (v) => { abortController?.abort(); abortController = new AbortController(); try { await debounceTimeout(1000, abortController.signal); add_li('timed val “' + v + '”'); } catch {} finally { abortController = null; } };
这个版本符合Web标准规范,取消逻辑和原生API保持一致,适合多异步任务协同的复杂场景。
你原有代码中的HTML部分无需修改,替换JS的实现逻辑即可正常运行。
内容的提问来源于stack exchange,提问作者Err488
相关产品推荐
相关产品推荐

