联动取消是指异步操作中止时,关联请求自动取消以避免资源浪费和状态不一致;核心工具是AbortController及其共享的AbortSignal,支持多请求同步取消、链式请求联动中断等场景。

什么是联动取消,为什么需要它
联动取消是指当某个异步操作被中止时,与之相关联的其他正在运行或即将发起的请求也自动取消,避免资源浪费和状态不一致。比如用户在搜索框连续输入,每次输入都触发一次 fetch 请求,但只有最后一次输入的结果有意义——前面未完成的请求就该被及时终止。AbortController 提供了统一的信号机制(AbortSignal),让多个 fetch 共享同一个取消逻辑,是实现联动取消的核心工具。
基础用法:单个 fetch 绑定 AbortController
每个 fetch 可通过 signal 选项接收一个 AbortSignal 实例。调用 controller.abort() 后,所有监听该 signal 的 fetch 会立即 reject 并抛出 AbortError。
示例:
const controller = new AbortController();
const { signal } = controller;
fetch('/api/data', { signal })
.then(res => res.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('请求已被取消');
} else {
console.error('其他错误:', err);
}
});
// 3 秒后主动取消
setTimeout(() => controller.abort(), 3000);
关键技巧:多请求共享同一 signal 实现联动
多个 fetch 可以共用同一个 AbortSignal,只要它们都传入相同的 signal 选项,就能做到“一触即停”。适用于以下典型场景:
- 搜索建议 + 主搜索结果两个接口需同步取消
- 分页加载中,新一页请求发出时,自动取消上一页未完成的请求
- 表单提交前校验(多个字段并行验证)+ 最终提交,任一环节取消则全部停止
示例(搜索建议 + 主搜索):
let currentController = null;
function performSearch(query) {
// 取消上一次未完成的请求链
if (currentController) currentController.abort();
currentController = new AbortController();
const { signal } = currentController;
// 并发请求,共享 signal
Promise.all([
fetch(`/api/suggestions?q=${query}`, { signal }).then(r => r.json()),
fetch(`/api/results?q=${query}`, { signal }).then(r => r.json())
])
.then(([sugs, results]) => {
renderSuggestions(sugs);
renderResults(results);
})
.catch(err => {
if (err.name !== 'AbortError') {
showError(err);
}
});
}
进阶实践:封装可取消的异步函数与组合逻辑
为提升复用性,可将 fetch 封装为返回 Promise 的函数,并支持传入 signal;再借助 Promise.race 或自定义组合器处理依赖关系。
例如:先获取 token,再用 token 请求数据,两者需联动取消:
async function fetchWithToken(url, options = {}) {
const controller = new AbortController();
const { signal } = controller;
try {
const tokenRes = await fetch('/api/token', { signal });
const { token } = await tokenRes.json();
const dataRes = await fetch(url, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${token}`
},
signal
});
return dataRes.json();
} catch (err) {
if (err.name === 'AbortError') throw err;
throw new Error(`请求失败: ${err.message}`);
}
}
// 使用时可外部控制
const controller = new AbortController();
fetchWithToken('/api/profile', { signal: controller.signal })
.catch(err => {
if (err.name === 'AbortError') console.log('整个链路已取消');
});
// 随时调用 controller.abort() 即可中断 token 获取和后续请求
注意:不能直接复用已 abort 的 controller,每次新流程必须新建实例。










