如何基于Promise实现可主动取消的sleep延时功能?
问题原因
你当前实现的bug根源很明确:clearTimeout仅会取消定时器的触发回调,不会对delay方法返回的Promise做任何状态变更,所以取消定时器后Promise会一直停留在pending状态,后续的await永远不会继续执行。
修正实现
核心调整逻辑:
- 取消延时的时候主动变更Promise状态,避免永久挂起
- 清理内部引用,避免内存泄漏
- 兼容重复调用取消、重复启动延时的边界场景
class Delayer { #timeoutId = null #resolveFn = null /** * 启动延时 * @param {number} ms 延时毫秒数 * @returns {Promise<boolean>} 延时正常结束返回true,主动取消返回false */ delay(ms) { // 启动新延时前自动清理上一次未完成的任务 this.cancelDelay() return new Promise(resolve => { this.#resolveFn = resolve this.#timeoutId = setTimeout(() => { this.#timeoutId = null this.#resolveFn = null resolve(true) }, ms) }) } /** * 主动取消未完成的延时 */ cancelDelay() { if (this.#timeoutId) { clearTimeout(this.#timeoutId) this.#timeoutId = null } if (this.#resolveFn) { this.#resolveFn(false) this.#resolveFn = null } } } // 使用示例 const delayer = new Delayer() // 启动3秒延时 const delayResult = await delayer.delay(3000) if (delayResult) { console.log('延时正常执行完成') } else { console.log('延时被主动取消') } // 任意位置调用cancelDelay即可终止延时,比如1秒后触发取消 setTimeout(() => { delayer.cancelDelay() }, 1000)
补充说明
- 代码中用ES私有字段(
#开头)存储定时器ID和Promise的resolve引用,避免外部误修改内部状态 - 你可以根据业务需求调整返回逻辑:比如取消时reject一个固定的CancelError,方便在catch分支单独处理取消逻辑
- 每次调用新的
delay时会自动取消上一次未完成的延时,避免多个延时任务冲突
内容的提问来源于stack exchange,提问作者maitrungdong
相关产品推荐
相关产品推荐

