js请求取消的核心是abortcontroller:因promise不可取消,需信号机制协同;fetch/axios默认不支持是因设计哲学不同;react中须在unmount、搜索变更、路由跳转时主动abort。

JS 中请求取消机制的面试题,核心是考察你对异步控制、资源清理和现代 API 设计的理解。回答时别堆概念,重点说清「为什么需要」「怎么实现」「边界怎么处理」,再带一两个典型场景对比,就能让面试官觉得你真用过、想过。
为什么 fetch 和 axios 默认不支持取消?
fetch 基于 Promise,而 Promise 一旦创建就无法中途终止;它只代表“一次异步操作的结果”,不提供状态干预能力。axios 底层用 XMLHttpRequest(XHR)时曾靠 abort() 实现取消,但默认行为仍是不可取消——除非你显式传入 AbortController 实例。
关键点:不是 API “没做”,而是设计哲学不同。Promise 的不可变性决定了它不适合承载可中断语义,所以需要额外信号机制来协同。
AbortController 是当前最标准的取消方案
它是 WHATWG 标准,被 fetch、XHR、甚至 setTimeout(实验性)原生支持,也是 React Query、SWR 等库内部取消逻辑的基础。
- 创建控制器:
const controller = new AbortController(); - 传入 signal:
fetch(url, { signal: controller.signal }) - 主动取消:
controller.abort()(触发AbortError) - 监听取消:
controller.signal.addEventListener('abort', ...)(少用,一般靠 catch 捕获)
注意:abort() 只影响本次请求,不会销毁 controller;重复调用无副作用,但 signal 会进入 aborted 状态,后续 fetch 会立即 reject。
如何封装一个带自动取消的请求函数?
常见错误是每次请求都新建 controller 却不暴露 cancel 方法,导致外部无法控制。高效封装要兼顾复用性与可控性:
- 返回对象含
promise和cancel方法,便于上层统一管理(如组件卸载时调用) - 利用闭包保存 controller,避免 signal 泄漏
- catch 中区分
err.name === 'AbortError',避免把取消误当真实错误上报
示例精简版:
function cancellableFetch(url, options = {}) {
const controller = new AbortController();
const promise = fetch(url, { ...options, signal: controller.signal })
.catch(err => {
if (err.name === 'AbortError') throw new Error('Request cancelled');
throw err;
});
return {
promise,
cancel: () => controller.abort()
};
}
React 场景下取消请求的典型时机
不是所有请求都需要手动取消,但以下情况必须处理,否则可能引发内存泄漏或状态错乱:
- 组件 unmount 前(useEffect cleanup):防止 setState on unmounted component
- 搜索输入频繁变更时(防抖+取消上一个):避免旧结果覆盖新请求
- 路由跳转前:比如详情页加载中用户点回列表页,应中断详情请求
小技巧:React 18 后 useTransition + startTransition 配合 Suspense 能缓解部分竞态问题,但底层仍依赖 signal 控制,不能替代取消逻辑。











