可封装通用异步重试函数,支持最大重试次数、指数退避、自定义超时和错误过滤,核心用Promise.race控制超时,递归实现重试逻辑。

可以封装一个通用的异步重试函数,支持最大重试次数、指数退避、自定义超时和错误过滤,核心是用 Promise.race 控制超时,用递归或循环控制重试逻辑。
基础重试 + 超时控制
最简版本:每次失败后立即重试(不退避),超时后拒绝 Promise:
function retryAsync(fn, options = {}) {
const { maxRetries = 3, timeout = 5000 } = options;
<p>return new Promise((resolve, reject) => {
const attempt = (n) => {
const timeoutId = setTimeout(() => {
reject(new Error(<code>Timeout after ${timeout}ms</code>));
}, timeout);</p><pre class="brush:php;toolbar:false;"> Promise.resolve()
.then(() => fn())
.then(result => {
clearTimeout(timeoutId);
resolve(result);
})
.catch(err => {
clearTimeout(timeoutId);
if (n >= maxRetries) reject(err);
else attempt(n + 1);
});
};
attempt(1);}); }
带指数退避与错误筛选的增强版
生产环境推荐:失败后延迟重试(如 100ms → 200ms → 400ms),并跳过特定错误(如 401、403)不重试:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
function retryAsync(fn, options = {}) {
const {
maxRetries = 3,
baseDelay = 100,
timeout = 5000,
shouldRetry = (err) => true, // 默认都重试
} = options;
<p>const execute = (n) => {
return Promise.race([
new Promise((resolve, reject) => {
const timeoutId = setTimeout(
() => reject(new Error(<code>Timeout after ${timeout}ms</code>)),
timeout
);</p><pre class="brush:php;toolbar:false;"> Promise.resolve()
.then(() => fn())
.then(result => {
clearTimeout(timeoutId);
resolve(result);
})
.catch(err => {
clearTimeout(timeoutId);
if (n > maxRetries || !shouldRetry(err)) {
reject(err);
} else {
const delay = baseDelay * Math.pow(2, n - 1);
setTimeout(() => execute(n + 1), delay);
}
});
}),
// 防止未 clearTimeout 导致内存泄漏的兜底
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Retry aborted')), timeout + 1000)
),
]);};
立即学习“Java免费学习笔记(深入)”;
return execute(1); }
-
shouldRetry 可传入函数判断是否重试,例如:
(err) => err?.response?.status >= 500 - 延迟使用 指数退避(
baseDelay × 2^(n−1)),避免雪崩重试 - 外层
Promise.race确保任意分支完成即结束,避免悬挂 Promise
配合 fetch 使用的实用示例
封装一个带重试的 fetch 请求工具:
const fetchWithRetry = (url, options = {}) =>
retryAsync(
() => fetch(url, { ...options, signal: AbortSignal.timeout(3000) }),
{
maxRetries: 2,
timeout: 5000,
shouldRetry: (err) =>
err.name === 'TypeError' || // 网络错误
(err?.response?.status >= 500 && err?.response?.status < 600),
}
);- 内部用
AbortSignal.timeout给单次 fetch 设超时,外层再用 retryAsync 控制整体超时和重试 - 只对网络异常和 5xx 错误重试,4xx(如 404、401)直接抛出
- 调用方式不变:
fetchWithRetry('/api/data').then(...).catch(...)
注意事项与优化点
- 避免在重试中重复创建资源(如新 AbortController),应在每次执行时新建
- 超时时间建议设为略大于单次请求预期耗时 × 2,防止过早中断
- 可扩展支持取消(传入
AbortSignal),但需修改调用链路 - 日志建议加在 catch 中,记录重试次数和错误原因,便于排查

















