Promise到底能不能取消?怎么取消?你知道吗?

先说说Promise是个啥?

想象一下,Promise就像是你点外卖后收到的那张小票------它不代表你已经拿到饭了,但代表餐厅已经接单,并且承诺(Promise)会给你送餐。

这张小票有三种可能的结果:

  1. 🟡 等待中 - 饭还在做,骑手还没到
  2. 已完成 - 饭送到了,可以开吃了
  3. 已拒绝 - 餐厅打电话说卖完了,做不了

那为啥Promise不能取消呢?

原因1:设计初衷不同

Promise的设计目标特别简单:只管最终结果(成功或失败),不管中间过程。

就像你点了外卖后:

  • 你只关心最后能不能吃到饭
  • 你不关心厨师怎么做饭、骑手怎么送餐
  • 取消订单是餐厅和平台的事,不是小票的功能

原因2:取消太复杂了

如果Promise能取消,会出现很多麻烦事:

javascript 复制代码
// 假如Promise能取消,会出现这种混乱情况:
const order = 点外卖();

order.then(吃饭); // 饭到了就吃
order.cancel();   // 突然取消

// 问题来了:
// 1. 如果饭已经在路上了怎么办?
// 2. 骑手接到取消通知需要时间,这期间饭送到了算谁的?
// 3. 钱怎么退?谁去跟餐厅沟通?

原因3:状态机不能乱改

Promise的状态变化特别简单:

lua 复制代码
     成功
🟡 ------> ✅
   失败
🟡 ------> ❌

一旦变成✅或❌,就再也不能变了。

如果加入取消功能,就变成了:

rust 复制代码
     成功         取消
🟡 ------> ✅   🟡 ------> 🚫
   失败
🟡 ------> ❌

这样复杂度直接翻倍,而且会产生各种边界情况需要处理。

那我真的需要取消功能怎么办?

虽然原生Promise不能取消,但我们有替代方案!

方案1:用AbortController取消网络请求(最常用)

javascript 复制代码
// 创建一个控制器
const controller = new AbortController();

// 发起请求时带上"取消开关"
fetch('/api/data', {
  signal: controller.signal // 把这个开关交给fetch
})
.then(response => response.json())
.then(data => console.log('收到数据:', data))
.catch(err => {
  if (err.name === 'AbortError') {
    console.log('请求被取消了!');
  } else {
    console.log('其他错误:', err);
  }
});

// 需要取消的时候
document.getElementById('cancel-btn').addEventListener('click', () => {
  controller.abort(); // 按下取消按钮!
});

方案2:自己写个可取消的Promise

javascript 复制代码
// 定义一个可取消的异步任务
function createCancellableTask(taskFn) {
  let isCancelled = false;       // 标记任务是否被取消
  let cancelCallbacks = [];      // 存放取消时需要执行的清理函数

  // 用 Promise 封装任务
  const promise = new Promise((resolve, reject) => {
    taskFn(
      (result) => {
        // 如果任务已取消,则返回一个特殊的错误对象
        if (isCancelled) {
          reject({ cancelled: true });
        } else {
          resolve(result); // 否则正常完成
        }
      },
      (error) => {
        // 如果任务已取消,同样返回特殊错误
        if (isCancelled) {
          reject({ cancelled: true });
        } else {
          reject(error); // 否则抛出原始错误
        }
      }
    );
  });

  return {
    promise,  // 暴露 promise,方便调用 then/catch
    cancel() {
      // 设置取消标志
      isCancelled = true;
      // 执行所有注册的清理函数
      cancelCallbacks.forEach(cb => cb());
    },
    onCancel(cb) {
      // 注册一个取消时的回调
      cancelCallbacks.push(cb);
    }
  };
}

// 使用示例
const { promise, cancel, onCancel } = createCancellableTask((resolve, reject) => {
  // 模拟一个异步任务:3 秒后完成
  const timer = setTimeout(() => resolve('Task finished!'), 3000);

  // 注册取消时的清理逻辑
  onCancel(() => {
    clearTimeout(timer);
    console.log('Timer cleared');
  });
});

// 监听任务的完成或失败
promise
  .then(result => console.log(result))
  .catch(error => {
    if (error.cancelled) {
      console.log('Task was cancelled'); // 捕获到取消
    } else {
      console.error('Task failed:', error); // 捕获到一般错误
    }
  });

// 2 秒后主动取消任务
setTimeout(cancel, 2000);

方案3:用Promise.race实现超时自动取消

javascript 复制代码
function 带超时的请求(原始Promise, 超时时间) {
  const 超时Promise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('请求超时了!')), 超时时间);
  });
  
  return Promise.race([原始Promise, 超时Promise]);
}

// 使用:5秒内没响应就自动取消
带超时的请求(fetch('/api/slow-data'), 5000)
  .then(响应 => 响应.json())
  .then(数据 => console.log(数据))
  .catch(错误 => console.log(错误.message));

总结一下

方案 适用场景 优点 缺点
AbortController 取消fetch请求 原生支持,最简单 只能用于fetch
自定义包装器 任何异步操作 最灵活,万能方案 需要自己实现
Promise.race超时 设置最大等待时间 实现简单 不是真正的取消
  1. 大部分时候,你其实不需要取消Promise,让它自然完成或失败就好
  2. 需要取消网络请求时,优先使用AbortController
  3. 特殊需求时,再考虑自己实现取消逻辑
相关推荐
夏幻灵1 小时前
HTML5里最常用的十大标签
前端·html·html5
Mr Xu_1 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝1 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions1 小时前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发1 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
程序员猫哥_1 小时前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞051 小时前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
我爱加班、、2 小时前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao2 小时前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架
杨超越luckly2 小时前
HTML应用指南:利用GET请求获取中国500强企业名单,揭秘企业增长、分化与转型的新常态
前端·数据库·html·可视化·中国500强