← 返回题目列表

前端请求超时如何实现?Fetch 为什么没有内置 timeout?

高频 中等 第 3 / 26 题 更新于 2026/08/03
前端网络请求超时FetchAbortController

简化版

Fetch 没有直接的 timeout 配置,通常用 AbortControllersetTimeout 实现超时取消。超时不是简单报错,还要区分连接慢、服务端慢、用户取消、业务错误和网络断开。工程上要设置合理超时时间、清理 timer、避免重复提示、对幂等请求可重试,对非幂等请求谨慎处理。

详细版

基础封装如下:

async function fetchWithTimeout(url: string, options: RequestInit = {}, timeout = 10000) {
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort('timeout'), timeout)
  try {
    return await fetch(url, { ...options, signal: controller.signal })
  } finally {
    clearTimeout(timer)
  }
}
说明
AbortController取消 fetch
setTimeout到点触发 abort
finally清理定时器
错误分类区分超时和其他异常

超时策略是用户体验和系统保护,不是让后端业务自动回滚的魔法。

完整版教学

一、为什么需要请求超时

网络请求可能因为弱网、服务端卡住、网关排队、连接异常而长时间不返回。如果没有超时,页面 loading 会一直转,用户不知道发生了什么。

超时可以让前端及时给出反馈,也能释放一些客户端资源。

二、Fetch 为什么没有 timeout 字段

Fetch 的设计更倾向于用 AbortController 统一处理取消,包括用户取消、路由切换、组件卸载和超时。

也就是说 timeout 是一种取消原因,不是独立的网络能力。

三、基础实现细节

实现时必须清理 timer。否则请求已经成功返回,定时器还在,后续触发 abort 会造成奇怪副作用或资源浪费。

try {
  const res = await fetch(url, { signal })
  return res
} finally {
  clearTimeout(timer)
}

finally 是最稳妥的清理位置。

四、错误分类

请求失败不等于超时。

类型示例处理
超时超过 10s提示重试或降级
用户取消切页、关闭弹窗通常静默
HTTP 错误500、404看业务提示
网络断开offline、DNS提示网络异常

错误分类可以改善用户提示和监控质量。

五、超时时间怎么定

不同接口不应一刀切。

  • 首屏关键查询:较短超时,失败给降级。
  • 大文件上传:更长超时,配合进度和断点续传。
  • 后台导出:可能改成异步任务轮询。
  • 写操作:超时后要查询结果,不能简单重复提交。

六、和重试的关系

超时后是否重试取决于请求是否幂等。GET 查询通常可以重试;POST 创建订单、支付确认等要谨慎,最好有幂等键。

if (isTimeout(error) && method === 'GET') {
  // 可以考虑重试
}

七、常见误区与追问

  • 误区:超时就是请求没有到服务端。 请求可能已经到达并被处理,只是响应没回来。
  • 误区:超时后可以无脑重试 POST。 写请求可能造成重复操作,必须有幂等设计。
  • 误区:Promise.race 超时就够了。 只 race 不 abort,底层请求可能还在继续。
  • 追问:为什么要 clearTimeout? 避免请求结束后定时器仍触发,造成资源浪费和误取消。
  • 追问:如何区分用户取消和超时? 可以记录 abort reason 或封装不同错误类型。
  • 追问:上传接口超时怎么设置? 上传耗时受文件大小和网络影响,应更长,并配合进度和断点续传。

八、加强记忆

请求超时记成“定时器触发 AbortController”。成功失败都清 timer;GET 可重试,POST 要幂等;超时只说明前端等不到,不代表后端没执行。