用单例 abortcontroller 复用同一 abortsignal,在 beforeunload/pagehide 时调用 controller.abort() 统一取消所有请求,并捕获 aborterror 静默处理,必要时支持 resetabortcontroller 动态重置。

用一个 AbortSignal 在页面卸载(如关闭、刷新、导航)时统一取消所有在途请求,关键在于共享信号并合理绑定生命周期——不需要为每个请求单独管理 AbortController,而是复用同一个信号源,并确保它在页面销毁前触发 abort()。
1. 创建并导出全局可访问的 AbortController
在模块顶层(如 apiClient.js 或 request.js)创建单例控制器,供所有请求复用:
let controller = new AbortController();
export const abortSignal = controller.signal;
// 页面卸载时统一中止
window.addEventListener('beforeunload', () => {
controller.abort();
});
// 可选:也监听 pagehide(更可靠,支持缓存页面、安卓后台等)
window.addEventListener('pagehide', (e) => {
if (!e.persisted) controller.abort();
});
⚠️ 注意:不要在每次请求时新建 AbortController;否则无法“一键”取消。复用同一实例才能集中控制。
2. 所有 fetch 请求都传入该 signal
无论你封装的是 fetch、axios 还是自定义 hook,只要底层支持 signal 选项,就统一传入 abortSignal:
// 示例:通用请求函数
export async function request(url, options = {}) {
return fetch(url, {
...options,
signal: abortSignal // ✅ 统一注入
});
}
// 使用
request('/api/users').then(...);
request('/api/posts').then(...);
✅ 即使多个请求同时发出,只要它们共用这个 signal,调用一次 controller.abort() 就会立刻拒绝全部未完成的 Promise。
3. 处理 abort 异常,避免未捕获错误
被中止的请求会抛出 DOMException(name === 'AbortError'),需显式捕获,否则可能触发 unhandledrejection:
- 在请求调用处
catch并忽略 abort 错误 - 或在统一请求封装层拦截处理:
export async function request(url, options = {}) {
try {
const res = await fetch(url, { ...options, signal: abortSignal });
return res;
} catch (err) {
if (err.name === 'AbortError') {
// 可选:打日志、埋点,但不 throw
console.debug('Request aborted on page unload');
return null; // 或 throw new CanceledError()
}
throw err;
}
}
4. 高级场景:动态重置信号(可选)
如果页面不销毁但需要“重置全部请求”(比如用户退出登录、切换租户),可以安全地重建控制器:
- 暴露一个
resetAbortController()方法 - 新请求自动使用新 signal,旧请求已 abort 或自然结束
- 注意:重置后需重新绑定
beforeunload等事件(或改用事件委托)
export function resetAbortController() {
controller.abort(); // 先中止旧的
controller = new AbortController();
// 无需重复绑定事件 —— 用 once 或检查是否已绑定
}
不复杂但容易忽略:信号复用 + 生命周期绑定 + 异常静默处理,三者缺一不可。做完这四步,页面关闭那一刻,所有 pending 请求真正“瞬间清零”。










