JavaScript请求重试应依据HTTP状态码语义判断:5xx及429可重试,4xx通常不重试;需处理网络异常,推荐指数退避、Retry-After头和幂等性保障。

在 JavaScript 请求重试逻辑中,是否重试不能一概而论,关键看 HTTP 状态码的语义:服务端错误(如 500、502、503、504)通常可重试,客户端错误(如 400、401、403、404)多数不应重试,除非业务明确要求(比如 429 限流需等待后重试)。
明确可重试的状态码范围
常见建议重试的状态码包括:
- 5xx 类:500(服务器内部错误)、502(网关错误)、503(服务不可用)、504(网关超时)——服务端临时故障,重试可能成功
-
429 Too Many Requests:限流响应,配合
Retry-After头可做延迟重试 -
网络异常或无响应(如
fetch抛出TypeError、AbortError)也应纳入重试范畴,虽非状态码,但属典型瞬时失败
避免重试的典型状态码
以下状态码一般不建议自动重试,重试不会改变结果,还可能引发副作用:
- 400 Bad Request:请求参数错误,改参数再发才有意义
- 401 Unauthorized / 403 Forbidden:认证失效或权限不足,需刷新 token 或提示用户操作
- 404 Not Found:资源不存在,重试无意义(除非是竞态导致的短暂 404,极少见)
- 405 Method Not Allowed / 409 Conflict:语义明确的客户端问题,应由业务逻辑处理
代码示例:基于状态码的 fetch 重试封装
下面是一个轻量封装,支持自定义重试状态码白名单和最大重试次数:
立即学习“Java免费学习笔记(深入)”;
function retryFetch(url, options = {}, {
retries = 3,
retryStatusCodes = [500, 502, 503, 504, 429],
baseDelay = 1000
} = {}) {
return fetch(url, options)
.then(res => {
if (retryStatusCodes.includes(res.status)) {
if (retries > 0) {
const delay = baseDelay * Math.pow(2, 3 - retries); // 指数退避
return new Promise(resolve => setTimeout(() => resolve(), delay))
.then(() => retryFetch(url, options, { retries: retries - 1, retryStatusCodes, baseDelay }));
}
}
return res; // 状态码不在重试列表,或已无重试次数,直接返回
})
.catch(err => {
// 网络错误、CORS、abort 等非响应错误
if (retries > 0 && err.name !== 'AbortError') {
const delay = baseDelay * Math.pow(2, 3 - retries);
return new Promise(resolve => setTimeout(() => resolve(), delay))
.then(() => retryFetch(url, options, { retries: retries - 1, retryStatusCodes, baseDelay }));
}
throw err;
});
}
使用时可灵活传入白名单,例如只重试 503 和 429:retryFetch('/api/data', {}, { retryStatusCodes: [503, 429] })
进阶建议:结合 Retry-After 和请求幂等性
对 429 或 503 响应,优先读取响应头 Retry-After 决定等待时间,比固定退避更精准;同时确保重试请求具备幂等性(如使用 GET 或带唯一 Idempotency-Key 的 POST),避免重复提交造成数据异常。


















