JS请求重试需先明确可重试场景(如网络中断、5xx错误)与禁重试场景(如400、401、非幂等POST),再控制次数、指数退避延迟、AbortController取消及异步判定,最后封装策略体系并集成可观测性与UI协同。

从写一个 try/catch + setTimeout 循环开始,到能在生产环境稳定支撑支付、上传、微服务调用等关键链路,JS 请求重试的学习路径其实很清晰:先理解“为什么重试”,再掌握“重试什么”和“怎么控制重试”,最后落地为可维护、可观测、可配置的工程能力。
搞懂重试的边界:不是所有失败都该重试
入门第一步不是写代码,而是建立判断意识。重试不是万能膏药,盲目重试会放大问题:
- ✅ 可重试场景:网络中断(
TypeError: Failed to fetch)、连接超时、5xx 服务端错误(如503 Service Unavailable)、响应状态码为 0(fetch 在离线时返回) - ❌ 禁止重试场景:400(参数错误)、401/403(鉴权失败)、404(资源不存在)、422(校验不通过);非幂等请求(如未带
Idempotency-Key的 POST 订单) - ⚠️ 需策略判断:5xx 中若含
Retry-After响应头,应按其指定时间延迟;token 过期需先刷新再重试——这就引出异步判定需求
掌握核心控制要素:次数、延迟、取消、判定
熟练的关键是把四个变量拆开练熟,再组合:
- 次数:默认设为 2(最多发起 3 次),移动端建议 ≤3,后台任务可放宽至 5;必须有硬上限,防止无限循环
-
延迟:不用固定 1s,改用指数退避(
base * 2^attempt),例如 200ms → 400ms → 800ms;叠加 ±25% 抖动防“重试风暴” -
取消:每次重试前新建
AbortController,传入signal;cancel 方法要同步清理定时器、abort 控制器、拒绝 pending promise -
判定:基础版用同步函数
shouldRetry(err, res) → boolean;进阶版支持异步判定(如async (err) => await checkTokenValid())
封装可复用的执行器:从函数到策略体系
别在每个 fetch 调用里重复写重试逻辑。分三步演进:
- 第一步:封装单个函数,如
retryFetch(url, options, { maxRetries: 2 }),内部用递归 Promise 或 for 循环 + await - 第二步:提取不可变策略对象,含
maxRetries、baseDelayMs、backoffFactor、jitter、shouldRetry字段,便于共享与测试 - 第三步:构建策略管理体系,提供
setGlobalRetryPolicy()、overridePolicy(/\/payment\//, { maxRetries: 0 })、cancelAllPendingRetries()等运行时接口
集成与观测:让重试真正可用、可信
上线前必须补上两块拼图:
-
与请求库对齐:对
fetch封装safeFetch;对axios用拦截器(请求加config.retry = true,响应拦截中触发重试),不推荐 monkey patchwindow.fetch -
内置可观测性:每次重试前打日志,如
[retry] /api/user, attempt 2/3, delay 420ms;失败时记录最终错误类型和堆栈;关键业务可上报重试成功率指标 - UI 层协同:静默重试第一次;第二次失败显示轻提示(如“网络稍慢,正在重试…”);第三次失败给出明确操作按钮,并考虑降级(如展示缓存数据)











