用Promise封装带自动重试的网络请求,核心是将fetch等请求逻辑封装为返回Promise的函数,通过递归或循环实现重试,支持指数退避、错误过滤、最大重试次数控制及AbortController取消。

用 Promise 包装带自动重试的网络请求,核心是把 fetch(或其他请求逻辑)封装进一个返回 Promise 的函数,并在失败时递归或循环调用自身,同时控制重试次数和延迟。
基础结构:Promise + 递归重试
最直接的方式是写一个返回 Promise 的函数,内部用 fetch 发起请求,成功则 resolve,失败则判断是否还能重试——能就延时后再次调用自己,不能就 reject。
关键点:
- 传入最大重试次数(maxRetries)和当前尝试序号(attempt,默认 0)
- 每次失败前检查 attempt < maxRetries,否则抛出错误
- 用 setTimeout 或 Promise.delay 实现退避延迟(如指数增长:1s、2s、4s…)
支持指数退避与错误过滤的实用版本
真实场景中,不是所有失败都要重试(比如 401 或 404 通常无意义),所以建议加一层错误判断:
立即学习“Java免费学习笔记(深入)”;
示例代码:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
function retryFetch(url, options = {}, maxRetries = 3, attempt = 0) {
return fetch(url, options)
.then(res => {
// 只对 5xx 或网络错误重试;401/403/404 等业务错误直接拒绝
if (res.ok || (res.status >= 400 && res.status < 500)) {
return res;
}
throw new Error(`HTTP ${res.status}`);
})
.catch(err => {
if (attempt >= maxRetries) throw err;
<pre class='brush:php;toolbar:false;'> const delay = Math.pow(2, attempt) * 1000; // 指数退避:1s, 2s, 4s...
return new Promise(resolve => setTimeout(resolve, delay))
.then(() => retryFetch(url, options, maxRetries, attempt + 1));
});}
用 async/await 写更清晰的循环重试
相比递归,用 for 循环 + await 更易读、不易栈溢出,也方便插入日志或取消逻辑:
示例:
async function retryFetch(url, options = {}, maxRetries = 3) {
for (let i = 0; i <= maxRetries; i++) {
try {
const res = await fetch(url, options);
if (res.ok || (res.status >= 400 && res.status < 500)) {
return res;
}
throw new Error(`HTTP ${res.status}`);
} catch (err) {
if (i === maxRetries) throw err;
const delay = Math.pow(2, i) * 1000;
await new Promise(r => setTimeout(r, delay));
}
}
}
进阶:包装成可复用的高阶函数
把重试逻辑抽离为装饰器,适配任意返回 Promise 的异步函数(不只是 fetch):
例如:
function withRetry(fn, maxRetries = 3, delayBase = 1000) {
return async function(...args) {
for (let i = 0; i <= maxRetries; i++) {
try {
return await fn(...args);
} catch (err) {
if (i === maxRetries) throw err;
await new Promise(r => setTimeout(r, Math.pow(2, i) * delayBase));
}
}
};
}
<p>// 使用
const safeFetch = withRetry(fetch, 3);
safeFetch('/api/data', { method: 'GET' })
.then(res => res.json())
.catch(console.error);</p>不复杂但容易忽略:重试前最好校验是否值得重试(如 network error、5xx)、避免无脑重试表单提交等幂等性敏感操作。实际项目中还可结合 AbortController 增加超时或手动取消能力。

















