基础重试采用循环+try/catch控制次数,仅对网络超时、502/503等可重试错误执行,避开400/401等业务错误;进阶策略引入指数退避与错误分类,避免惊群效应并精准识别网络或5xx错误;并发场景用Promise.allSettled配合独立重试;生产环境需加入超时控制与AbortSignal防止卡死。

基础重试:用循环 + try/catch 控制次数
最直接的方式是把 await 操作包进 for 循环,配合 try/catch 捕获每次失败。关键点在于:只对可重试的错误(比如网络超时、502/503)重试,而 400、401 等业务错误通常不该重试。
示例代码:
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i try {
const res = await fetch(url);
if (!res.ok) {
const error = new Error(`HTTP ${res.status}`);
if ([502, 503, 504].includes(res.status)) throw error;
else throw error;
}
return await res.json();
} catch (err) {
if (i === maxRetries - 1) throw err;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // 递增延迟
}
}
}
进阶策略:指数退避 + 错误分类
固定等待 1 秒容易引发请求洪峰(“惊群效应”),更稳妥的做法是采用指数退避:第 1 次失败等 1s,第 2 次等 2s,第 3 次等 4s……同时区分错误类型,避免对 400、401、403 等客户端错误重试。
立即学习“Java免费学习笔记(深入)”;
建议做法:
- 用 err.name 或 err.message 判断是否为网络类错误(如 'TypeError: Failed to fetch')
- 检查 res.status 是否属于服务端临时错误(5xx)
- 对非重试类错误(如 token 过期、参数错误)立即抛出,不进入重试循环
并发请求的重试:Promise.allSettled 配合单独封装
当多个异步任务并行执行,又需要各自独立重试时,不要用 Promise.all —— 它一错全错。改用 Promise.allSettled,再为每个任务单独加重试逻辑。
例如:
const results = await Promise.allSettled([
fetchWithRetry('/api/user'),
fetchWithRetry('/api/orders'),
fetchWithRetry('/api/profile')
]);
results.forEach((r, i) => {
if (r.status === 'rejected') console.warn(`第 ${i+1} 个请求最终失败`, r.reason);
});
生产级补充:超时控制 + 取消信号
真实场景中,重试不能无限等下去。建议给 fetch 加上 AbortSignal.timeout()(现代浏览器支持),或手动封装超时逻辑:
const controller = new AbortController();
setTimeout(() => controller.abort(), 8000);
const res = await fetch(url, { signal: controller.signal });
这样即使重试中某次卡死,也能在指定时间后终止,避免阻塞整个流程。


















