JavaScript 取消已启动的 Promise 实用方法详解

Promise 无法原生取消

JavaScript 原生 Promise 一旦创建,内部的异步流程便会继续执行,并且没有提供像 .cancel() 这样的接口来中断它。你只能“忽略其结果”,但无法停止底层操作。

合理取消的核心思路

虽然无法直接取消 Promise,但可以通过控制异步操作来实现“间接取消”:

  • 取消底层操作(如 HTTP 请求、定时器等)
  • 阻止 Promise 处理继续
  • 清理不再需要的资源

实用方法详解

1. AbortController + fetch / XHR / Node API

标准方式:通过 AbortController 生成一个 signal,传递给支持它的异步操作(如 fetch)。当调用 controller.abort() 时,会触发 AbortError,中止操作并使 Promise 拒绝。

const controller = new AbortController();

async function fetchData(url, signal = controller.signal) {
  const res = await fetch(url, { signal });
  return res.json();
}

fetchData('/api/data')
  .then(console.log)
  .catch(err => {
    if (err.name === 'AbortError') console.warn('请求已取消');
    else console.error(err);
  });

// 在 2 秒后取消请求
setTimeout(() => controller.abort(), 2000);

适用情境:HTTP 请求、Web API、部分 Node.js API。

2. Promise.race + 自定义“超时”Promise

对于不支持 AbortController 的场景,可使用 Promise.race() 与一个可拒绝的“超时”Promise 组合,以实现超时或取消效果 。

function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('超时取消')), ms)
  );
  return Promise.race([promise, timeout]);
}

// 使用示例:
withTimeout(fetch('/api/data'), 3000)
  .then(console.log)
  .catch(err => console.warn(err.message));

注意:底层操作仍在运行,只是结果不再被处理。

3. 自定义可取消任务(Promise.withResolvers / Token 模式)

利用 Promise.withResolvers() 或手动封装,构建带 cancel() 方法的可控任务。

function buildCancelable(asyncFn) {
  let canceled = false;
  const { promise, resolve, reject } = Promise.withResolvers();

  asyncFn().then(
    res => canceled ? reject(new Error('取消处理')) : resolve(res),
    err => reject(err)
  );

  return {
    promise,
    cancel() { canceled = true; reject(new Error('已取消')); }
  };
}

// 使用
const task = buildCancelable(async () => {
  await new Promise(r => setTimeout(r, 5000));
  return 'Hello';
});
task.promise
  .then(console.log)
  .catch(err => console.warn(err.message));

setTimeout(() => task.cancel(), 2000);

优点:控制更细;缺点:底层逻辑仍执行,不是真正中断。

4. 采用 RxJS Observables

当需要更复杂的流式异步处理,并支持取消订阅时,可以转向 RxJS 的 Observable 模式,其本身具备取消机制:

import { Observable } from 'rxjs';

const obs = new Observable(sub => {
  const id = setTimeout(() => sub.next('done'), 3000);
  return () => clearTimeout(id); // 取消时清理
});
const sub = obs.subscribe(val => console.log(val));
setTimeout(() => sub.unsubscribe(), 2000);

总结

  • 原生 Promise 不能直接取消,要通过封装或外部控制底层操作。
  • AbortController 是当前最优解,简洁高效。
  • Promise.race 超时模式适用于快速忽略结果。
  • RxJS 更适合复杂流式异步处理。
评论