侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现一个可取消的延迟Promise

2025-12-14 / 0 评论 / 4 阅读

题目

实现一个可取消的延迟Promise

信息

  • 类型:问答
  • 难度:⭐⭐

考点

Promise封装, 异步控制, 取消机制设计, ES6+特性应用

快速回答

实现要点:

  • 创建返回{ promise, cancel }对象的函数
  • 使用setTimeout实现延迟逻辑
  • 在Promise内部保存reject方法供外部调用
  • 取消时使用特定错误对象标识取消状态
  • 添加状态保护防止重复执行
## 解析

原理说明

原生Promise不支持取消操作,需利用闭包特性保存reject函数。核心设计:

  1. 创建pending状态的Promise
  2. 通过闭包暴露reject方法作为取消触发器
  3. 添加状态锁防止多次resolve/reject
  4. 使用特定错误类型区分正常错误和取消操作

代码示例

function createCancelableDelay(duration) {
  let cancelHandler = null;
  let isCancelled = false;

  const promise = new Promise((resolve, reject) => {
    cancelHandler = reject;  // 保存reject引用

    setTimeout(() => {
      if (!isCancelled) {
        resolve(`Completed after ${duration}ms`);
      }
    }, duration);
  });

  const cancel = (reason = 'DELAY_CANCELLED') => {
    if (!isCancelled) {
      isCancelled = true;
      cancelHandler(new Error(reason));  // 触发拒绝
    }
  };

  return { promise, cancel };
}

// 使用示例
const { promise, cancel } = createCancelableDelay(2000);

promise
  .then(console.log)
  .catch(err => {
    if (err.message === 'DELAY_CANCELLED') {
      console.log('Delay was cancelled');
    } else {
      console.error('Real error:', err);
    }
  });

// 在1秒后取消
document.getElementById('cancelBtn').addEventListener('click', cancel);

最佳实践

  • 错误区分:使用自定义错误类型(如CancelError)替代原始Error
  • 状态清理:取消后清除定时器避免内存泄漏
  • API设计:返回对象包含promise和cancel方法,符合人体工学
  • Typescript支持:添加类型声明增强安全性

常见错误

错误类型现象解决方案
未处理取消拒绝导致UnhandledPromiseRejection必须添加catch处理
多次取消调用重复触发reject添加状态锁(isCancelled)
未清除定时器内存泄漏在cancel中调用clearTimeout

扩展知识

  • AbortController:浏览器原生取消方案,需配合fetch API使用
  • RxJS Observable:响应式编程库提供完善取消机制
  • Promise.race():可通过竞争实现超时取消(需额外Promise)
  • React useEffect:在组件卸载时自动取消异步操作的实际应用场景