自动重试机制需封装fetch,仅对TypeError、AbortError及5xx等可恢复错误重试;采用指数退避(如500ms×2ⁿ⁻¹)加随机抖动,限3–5次;非幂等请求须确保后端幂等或前端交互确认。

网络不稳定时,fetch 请求失败很常见。自动重试机制不是简单地“失败就再发一次”,而是要控制重试次数、间隔、判断哪些错误值得重试,并避免无限重试或雪崩。核心是封装 fetch,用递归或循环 + 延迟来实现可控重试。
只对可重试的错误类型重试
不是所有失败都要重试。比如 401(未登录)、403(无权限)、404(资源不存在)属于业务逻辑错误,重试没意义;而网络中断、502/503/504 网关错误、超时、AbortError 等才适合重试。
- 捕获 TypeError(如 "Failed to fetch"、"NetworkError")——典型网络问题
- 检查响应状态码:5xx 错误(服务端临时故障)可重试,4xx 中仅少数如 429(限流)可考虑重试
- 手动触发的 AbortSignal.timeout() 超时也应纳入重试范围
带退避策略的延迟重试
连续快速重试可能加重服务端压力或客户端负担。推荐使用指数退避(Exponential Backoff):第 n 次重试前等待 base × 2ⁿ⁻¹ 毫秒,再加少量随机抖动防并发冲击。
- 例如 base = 500ms:第1次等 500ms,第2次等 ~1000–1200ms,第3次等 ~2000–2500ms
- 用 setTimeout + Promises 实现延迟,避免阻塞主线程
- 最大重试次数建议设为 3–5 次,超过即放弃并抛出最终错误
封装一个可复用的 retryFetch 函数
把重试逻辑抽成独立函数,支持传入 URL、init 配置、最大重试次数、基础延迟等参数,返回 Promise。
示例代码片段(无第三方依赖):async function retryFetch(url, options = {}, { retries = 3, baseDelay = 500 } = {}) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), options.timeout || 8000);
<p>try {
const res = await fetch(url, {
...options,
signal: controller.signal
});</p><pre class="brush:php;toolbar:false;">clearTimeout(timeoutId);
// 5xx 服务端错误重试,4xx 大部分不重试(除特定场景)
if (res.status >= 500 && res.status < 600 && retries > 0) {
throw new Error(`HTTP ${res.status}`);
}
return res;
} catch (err) { clearTimeout(timeoutId);
// 不重试的情况:显式取消、4xx(非429)、非网络类 TypeError
if (err.name === 'AbortError' ||
(err instanceof TypeError && !err.message.includes('fetch')) ||
(retries === 0)) {
throw err;
}
// 指数退避 + 抖动
const delay = baseDelay * Math.pow(2, (3 - retries)) + Math.random() * 200;
await new Promise(r => setTimeout(r, delay));
return retryFetch(url, options, { retries: retries - 1, baseDelay });} }
使用时注意请求幂等性与副作用
GET 请求天然幂等,重试安全;但 POST/PUT/DELETE 若含创建、扣款、发消息等操作,重复提交会导致数据异常。务必确保后端接口支持幂等(如通过 Idempotency-Key Header),或前端在重试前做去重/节流。
- 对非幂等请求,可考虑降级策略:先提示用户“网络不佳,是否重试?”,而非全自动重试
- 在请求头中带上唯一 Idempotency-Key,后端据此判断是否已处理
- 避免在重试中重复生成 body(如 JSON.stringify(new Date())),应提前固化数据


















