js请求重试易出错的关键在于边界失控:未捕获超时/网络错误(fetch不reject 4xx/5xx)、aborterror判定不兼容、可重试错误误判(如400/401)、递归无终止、共享计数器、定时器未清理、abortcontroller未abort导致请求堆积。

JS 请求重试机制本身不难实现,但上线后常因边界没控住、错误没分清、状态没清理,导致重试失效、请求堆积、无限循环甚至页面卡死。关键不在“怎么写”,而在“哪些地方容易错”。
重试未触发:超时或错误没被捕获
最常见的是写了重试逻辑,但实际一次都没重试——因为错误根本没进 catch 或没被判定为可重试。
- fetch 默认不 reject 网络失败,也不 reject 4xx/5xx 响应,需手动检查
res.ok或res.status - AbortController 触发的
AbortError在不同浏览器报错信息不一致(如 Chrome 是AbortError,Safari 可能是DOMException),不能只靠err.name === 'AbortError'判定 - 用 axios 时,
error.response存在 ≠ 可重试(比如 401/403 一般不该重试),要结合状态码和业务语义判断
重试失控:无限重试或重复发起
没有终止条件或状态隔离,一个失败请求可能触发几十次调用,压垮后端或阻塞主线程。
- 递归重试函数若未正确递减
retries参数,或退避 delay 写成固定值(如始终setTimeout(..., 100)),极易造成雪崩 - 多个并发请求共用同一套重试计数器(例如闭包变量或全局变量),会导致本该独立重试的请求互相干扰
- 未清除定时器或 AbortController,旧请求未 abort 就发新请求,造成“幽灵请求”堆积
重试掩盖真问题:错误类型混淆
把不可重试错误(如参数错误、权限不足)当成网络问题反复重试,既无效又误导排查方向。
- 400 错误通常是前端传参格式错,重试无意义;应优先校验入参,而非加 retry
- 401/403 表示鉴权失效,应走 token 刷新流程,而不是等待几秒再重发原请求
- JSON 解析失败(
res.json() 报 SyntaxError)属于响应体异常,不是网络层问题,retry 前需确认接口是否稳定返回合法 JSON
环境与兼容性陷阱
本地跑得通,一上生产就失效,往往卡在环境差异上。
- AbortController 在 IE 完全不支持,若项目需兼容,必须降级方案(如用 Promise.race + setTimeout 模拟)
- Next.js SSR 环境中使用 fetch + AbortController,服务端 Node.js 版本若低于 18,
signal选项可能被忽略,超时不生效 - 某些代理或网关会吞掉
Abort信号,请求实际仍在后端运行,前端以为已中断,结果重试后出现重复提交











