← 返回题目列表

前端如何取消请求?为什么需要取消请求?

高频 中等 第 6 / 26 题 更新于 2026/07/28
前端网络取消请求AbortController竞态

简化版

前端可以用 AbortController 取消 fetch 请求,也可以用 axios 的取消能力。取消请求常用于组件卸载、搜索输入切换、路由跳转、避免旧请求覆盖新结果。它能减少无效工作,也能解决请求竞态问题。

详细版

需要取消请求的场景:

  • 用户离开页面,请求结果不再需要。
  • 搜索框输入变化,旧关键词请求应取消。
  • 路由快速切换,避免旧页面请求回来更新新页面。
  • 上传下载需要用户主动取消。

fetch 示例:

const controller = new AbortController()
fetch(url, { signal: controller.signal })
controller.abort()

取消请求后通常会抛出 AbortError,需要和普通错误区分处理。

完整版教学

一、为什么取消请求重要

请求发出后,用户可能已经切换页面或改变输入。旧请求如果回来后继续更新状态,可能造成错误展示。

例如搜索框先搜 a,再搜 abc。如果 a 的请求更晚返回,可能覆盖 abc 的结果。这就是竞态。

取消旧请求或忽略旧响应都可以解决这个问题。

二、AbortController

AbortController 是现代浏览器取消异步操作的标准方式。它通过 signal 把取消信号传给 fetch。

当调用 abort() 时,fetch 会被中止,并进入 reject。

代码中要识别 AbortError,避免把用户主动取消当成异常弹错。

三、取消和忽略的区别

取消请求是尽量终止网络和后续处理。忽略响应是不一定终止请求,但返回后判断它是否已经过期,过期就不更新 UI。

有些请求已经到达服务器,前端 abort 不代表服务端一定停止处理。因此关键幂等和状态一致性仍要服务端保证。

四、面试追问与工程落地

面试官可能问:“组件卸载后请求返回 setState 报错怎么办?”

React/Vue 中可以在卸载时 abort 请求,或维护一个已卸载标记,返回后判断是否还允许更新。更推荐封装请求 hooks,把取消逻辑统一处理。

工程中搜索、筛选、自动补全这类高频请求特别需要处理竞态。

五、取消信号、超时与竞态要分开

AbortController 可以让多个任务共享一个 signal,也能把“用户取消”和“超时取消”统一成控制流。调用 abort 后,fetch Promise 拒绝,但响应体读取阶段也可能因信号中止而失败。

async function load(url, signal) {
  try {
    const response = await fetch(url, { signal })
    if (!response.ok) throw new Error(`HTTP ${response.status}`)
    return await response.json()
  } catch (error) {
    if (signal.aborted) return { cancelled: true, reason: signal.reason }
    throw error
  }
}
手段浏览器是否停止等待防旧结果覆盖服务端是否必停
abort尽力停止不保证
请求序号
超时 Promise取决于是否联动 abort可能
服务端取消协议视实现可协作停止

取消是资源管理,竞态控制是状态正确性;可靠实现常把 abort 与“只接受最新请求”同时使用。

六、用搜索时间线验证最后写入

用户在 0ms 输入 a,100ms 输入 ab,200ms 输入 abc。三个接口分别在 900ms、500ms、300ms 后返回,则返回顺序可能是 abc → ab → a;若每次都 setState,最终页面反而显示最旧的 a

请求编号:1(a) ─────────────900ms
         2(ab) ─────500ms
         3(abc) ─300ms  ← 只有编号 3 可更新

每次新搜索先 abort 旧 controller,并记录 latestId;响应回来还要比较 id,覆盖极短窗口和无法真正取消的适配器。组件卸载时取消并清理 loading,取消不应弹“网络错误”。

写请求尤其不能把前端取消当回滚。订单创建可能已在服务器提交,即使浏览器断开也要通过幂等键、查询状态和事务确认最终结果。

七、常见误区与追问

  • 误区:调用 abort 后服务器一定停止处理。 请求可能已经到达并提交,浏览器只能保证自己的等待和流处理被中止。
  • 误区:只要取消旧请求就无需序号检查。 适配器可能不支持取消,或响应正处于完成竞态,最新编号仍是保险。
  • 误区:AbortError 应和网络失败一样提示。 用户主动取消是预期控制流,通常不应弹错误。
  • 追问:一个 signal 能否控制多个 fetch? 可以,abort 后所有监听该 signal 的未完成操作都会收到取消。
  • 追问:请求超时如何实现? 定时触发 controller.abort,并在 finally 清定时器,同时区分超时与用户取消原因。
  • 追问:为什么取消后还要恢复 loading? 网络停止不等于 UI 状态自动复原,清理逻辑要放 finally 或统一状态机。
  • 追问:写请求取消后如何确认结果? 使用幂等键并查询服务端最终状态,不能仅凭客户端 Promise 拒绝判断失败。

八、加强记忆

  1. 触发场景:卸载、路由切换、搜索换词、上传下载取消。
  2. 标准工具:AbortController 产生 signal,调用 abort 广播取消。
  3. 错误分流:取消、超时、网络错误和 HTTP 错误分别建模。
  4. 竞态保险:abort 旧请求,同时只允许最新请求编号写 UI。
  5. 生命周期:清理 loading、进度、定时器和闭包引用。
  6. 服务端边界:前端取消不等于事务撤销,写操作仍靠幂等与状态查询。