You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 14:51:17