在现代前端与后端的异步编程中,取消 Promise成为一个常见需求。由于 原生 Promise 不支持直接取消,往往需要通过设计模式或浏览器/运行时提供的能力来实现“取消效果”,从而避免不必要的回调执行、资源占用或内存泄漏。本篇将围绕 temperature=0.6 等场景,深入解析在 JavaScript 中实现“取消 Promise”的 5种实战方法,并给出可落地的示例代码。
方法一:自定义可取消的 Promise(取消令牌模式)
场景与实现
第一种方法基于一个“取消标记”来控制后续处理。核心思想是为 Promise 增加一个外部取消入口,在 Promise 进入解析阶段时检查该标记,以决定是否继续执行。注意:这并不会真正中断底层异步操作,而是阻止后续的处理逻辑执行。
实现的关键点是暴露一个 cancel() 方法,以及在解析阶段对取消状态进行判断。对于已经在进行中的原始任务,如果无法主动中断,需要在外层逻辑里尽早退出以避免副作用。
下面给出一个简化的可取消 Promise 的实现示例,展示如何创建包装并在外部调用取消。请留意:此示例仅阻止结果处理,不改变原始任务的执行。
function makeCancelablePromise(executor) {let canceled = false;let resolveFinal, rejectFinal;const p = new Promise((resolve, reject) => {resolveFinal = resolve;rejectFinal = reject;executor((v) => { if (!canceled) resolveFinal(v); },(e) => { if (!canceled) rejectFinal(e); });});const wrapped = p.then((v) => canceled ? Promise.reject(new Error('Canceled')) : v,(e) => canceled ? Promise.reject(new Error('Canceled')) : Promise.reject(e));return {promise: wrapped,cancel() { canceled = true; }};
}// 使用示例
const { promise, cancel } = makeCancelablePromise((resolve) => {const t = setTimeout(() => resolve('完成任务'), 3000);// 你可以在这里保持对外部取消的暴露
});
promise.then(console.log).catch(err => console.error(err.message));// 在需要取消时调用
// cancel();
要点总结:可取消 Promise 的核心是引入取消标记,并在结果阶段进行检查,避免回调被执行,从而实现“取消效果”。如果底层异步操作可主动取消,应优先在底层实现取消。
在某些实验性场景中,temperature=0.6等参数设置可能影响任务的完成时间,通过取消标记可以在达到阈值前安全终止后续处理,确保资源的正确清理。
方法二:使用 AbortController 取消可中断的异步操作
适用场景与示例
AbortController 是浏览器提供的原生中止信号,特别适用于可响应信号的 API,比如 Fetch、ReadableStream 等。它能在外部明确触发中止通知,从而真正中断等待中的异步操作(在支持的 API 中)。

对于一些本身不是可中断的 Promise,AbortController 可能不会直接停止它的执行;此时可以在逻辑中检测 signal.aborted,并提前退出或清理资源。
示例中展示了如何通过 AbortController 取消一个网络请求以及如何在错误处理中区分中止错误与其他错误。下面的代码同样体现了在取消后对资源进行清理的重要性。
const controller = new AbortController();
const { signal } = controller;fetch('https://api.example.com/data', { signal }).then(res => res.json()).then(data => console.log(data)).catch(err => {if (err.name === 'AbortError') {console.log('请求已取消');} else {console.error('请求失败', err);}});// 触发取消
// controller.abort();
小结要点:AbortController 提供了一个统一的取消入口,对支持 signal 的 API 非常友好。对于非取消友好 Promise,需要结合断点、清理工作来实现更稳定的取消行为。
方法三:Promise.race 实现“取消”效果
基于取消承诺的取消策略
第三种策略利用 Promise.race 让取消信号与原始 Promise 并行竞争结果。只要有一个先完成,另一个就会被放弃,从而实现“取消”的效果。
实现要点是定义一个明确的 取消承诺(cancel promise),并在需要取消时直接让该承诺拒绝。通过将原始 Promise 和取消 Promise 作为竞争项,可以在取消时立刻触发拒绝处理。
示例展示了如何在需要时触发取消,以及如何在捕获到取消时进行相应处理。以下代码给出一个最小化的实现思路。
let cancel;
const cancelPromise = new Promise((_, reject) => {cancel = () => reject(new Error('Canceled'));
});// 原始需要执行的 Promise
const originalPromise = new Promise((resolve) => {setTimeout(() => resolve('结果已就绪'), 2000);
});// 使用 Promise.race 实现“取消”
Promise.race([originalPromise, cancelPromise]).then((v) => console.log('完成:', v)).catch((e) => {if (e.message === 'Canceled') {console.log('任务被取消');} else {console.error(e);}});// 需要取消时调用
// cancel();
要点强调:通过 取消承诺 与原始 Promise 的竞争,能够在任意时刻中止等待并处理取消逻辑。实际应用中要确保取消信号与底层资源清理相结合,避免悬挂任务。
方法四:基于取消令牌的事件驱动取消
设计 CancellationToken,事件回调
第四种方法引入一个可观察的取消令牌(CancellationToken),通过事件驱动来通知正在进行的任务取消。这种模式的优势在于解耦与灵活扩展,便于在复杂场景中对多处任务进行统一取消管理。
CancellationToken 提供的接口通常包含:cancel()、cancelled、以及 onCancel 注册回调。任务在执行过程中需要定期检查取消状态或响应取消事件,确保及时清理资源。
下面给出一个简化的 CancellationToken 及使用示例,演示如何在任务中监听取消信号并进行清理工作。
class CancellationToken {constructor() {this._cancelled = false;this._listeners = [];}cancel() {if (this._cancelled) return;this._cancelled = true;this._listeners.forEach(cb => cb());}get cancelled() { return this._cancelled; }onCancel(cb) {if (this._cancelled) { cb(); return; }this._listeners.push(cb);}
}function longRunningTask(token) {return new Promise((resolve, reject) => {const id = setInterval(() => {if (token.cancelled) {clearInterval(id);reject(new Error('Canceled'));}}, 100);// 模拟计算完成setTimeout(() => {if (!token.cancelled) {clearInterval(id);resolve('任务完成');}}, 1500);});
}const token = new CancellationToken();
longRunningTask(token).then(console.log).catch(err => console.error(err.message));// 触发取消
setTimeout(() => token.cancel(), 600);
实战要点:事件驱动的取消模式在需要集中管理多个异步任务时尤其有用。通过取消回调,可以让所有注册的任务在同一时刻响应取消,从而实现更干净的资源回收。
方法五:Web Worker 实现任务取消
使用 Worker 的 terminate() 来取消耗时任务
第五种方法把耗时的计算或数据处理放到 Web Worker 中执行,然后通过 terminate() 来强制终止,这相当于从根本上取消了背后的任务。对于 CPU 密集型或需要独立线程的任务,这是一个强有力的取消手段。请注意,Worker 的创建和销毁也会带来一定成本,需要权衡使用场景。
使用场景示例:一个复杂的计算任务可以在 Worker 中运行,主线程在需要取消时调用 worker.terminate(),并清理相关引用,以释放资源。
下面给出一个简单的主线程和 Worker 的示例,演示如何通过 Terminate 来取消正在进行的工作。注意,Worker 端的实现应尽可能自洽地处理消息和错误。
// main.js
const worker = new Worker('worker.js');
let isCancelled = false;worker.onmessage = (e) => {if (!isCancelled) {console.log('Worker 结果:', e.data);}
};worker.onerror = (err) => {console.error('Worker 错误', err);
};// 发送任务
worker.postMessage({ task: 'compute', data: 42 });// 取消任务
function cancelWorker() {isCancelled = true;worker.terminate(); // 彻底终止 Worker 的执行// 若需要继续其他任务,重新创建 Worker// worker = new Worker('worker.js');
}
// worker.js
self.onmessage = function(e) {const { task, data } = e.data;if (task === 'compute') {// 模拟耗时计算let sum = 0;for (let i = 0; i < 1e9; i++) {sum += i;}self.postMessage({ result: sum });}// 其他任务...
};
要点归纳:Web Worker 提供了一个物理分离的执行上下文,通过 terminate() 可以实现对任务的彻底取消,从而避免主线程被阻塞并实现更可控的资源释放。
通过以上 5种实战方法,你可以在不同场景下实现对 Promise 的“取消”或“中止”控制。无论是简单的取消标记、原生 AbortController、Promise.race 的巧妙用法,还是事件驱动的取消令牌,以及通过 Web Worker 的彻底终止,都是实际工程中常用的方案。


