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 小时前
DevEco Studio与Web联合开发:打造鸿蒙混合应用的全景指南
开发语言·前端·华为·harmonyos·鸿蒙
衬衫chenshan2 小时前
【CTF】强网杯2025 Web题目writeup
前端
飞翔的佩奇2 小时前
【完整源码+数据集+部署教程】【天线&水】舰船战舰检测与分类图像分割系统源码&数据集全套:改进yolo11-repvit
前端·python·yolo·计算机视觉·数据集·yolo11·舰船战舰检测与分类图像分割系统
哆啦A梦15883 小时前
点击Top切换数据
前端·javascript·vue.js
程序猿追3 小时前
Vue组件化开发
前端·html
艾德金的溪4 小时前
redis-7.4.6部署安装
前端·数据库·redis·缓存
小光学长4 小时前
基于Vue的2025年哈尔滨亚冬会志愿者管理系统5zqg6m36(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
@PHARAOH4 小时前
WHAT - 受控组件和非受控组件
前端·javascript·react.js
生莫甲鲁浪戴4 小时前
Android Studio新手开发第二十六天
android·前端·android studio
JH30735 小时前
B/S架构、HTTP协议与Web服务器详解
前端·http·架构