标准答案

  1. AbortController 可以创建 controller 和 signal。把 signal 传给 fetch 后,调用 controller.abort() 就能取消请求。
  2. 请求被取消后 fetch 会 reject,错误通常是 AbortError。界面上应把它当成主动取消,而不是普通失败提示给用户。
  3. 常见场景包括组件卸载时取消请求、搜索输入变化时取消上一次请求、切换页面时清理异步任务、给请求设置超时。
  4. 请求竞态是指后发请求不一定后返回,旧响应可能覆盖新状态。处理方式可以是发新请求前 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 或更新响应式状态。

作者信息