标准答案
- AbortController 可以创建 controller 和 signal。把 signal 传给 fetch 后,调用 controller.abort() 就能取消请求。
- 请求被取消后 fetch 会 reject,错误通常是 AbortError。界面上应把它当成主动取消,而不是普通失败提示给用户。
- 常见场景包括组件卸载时取消请求、搜索输入变化时取消上一次请求、切换页面时清理异步任务、给请求设置超时。
- 请求竞态是指后发请求不一定后返回,旧响应可能覆盖新状态。处理方式可以是发新请求前 abort 旧请求,也可以给请求打序号,只接受最新序号的结果。AbortController 解决取消能力,序号或状态机解决结果归属。
题目解析
取消请求和防止竞态不是完全同一件事。abort 可以尽量停止旧请求,但如果请求已经返回,业务仍要判断这个结果是否还是当前有效结果。
AbortSignal 不只用于 fetch,越来越多异步 API 支持 signal。理解 signal 的传递方式后,可以把“取消”做成统一能力。
超时通常可以用 setTimeout 调用 abort 实现。真正生产代码还要在 finally 里清理定时器,避免任务已经完成后定时器仍然触发。
代码示例
下面示例同时处理取消旧请求、只接收最新结果和忽略 AbortError。
JavaScript
let currentController = null
let latestRequestId = 0
async function search(keyword) {
currentController?.abort()
const requestId = latestRequestId + 1
latestRequestId = requestId
currentController = new AbortController()
try {
const response = await fetch('/api/search?q=' + encodeURIComponent(keyword), {
signal: currentController.signal
})
const data = await response.json()
if (requestId === latestRequestId) {
renderResult(data)
}
} catch (error) {
if (error.name !== 'AbortError') {
showError(error)
}
}
}常见误区
- 取消请求后把 AbortError 当成普通错误弹给用户。
- 只取消请求,不处理已经返回但过期的响应。
- 组件卸载后仍然 setState 或更新响应式状态。