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 更适合复杂流式异步处理。