在网络请求或其它不可靠的异步操作中,失败是常事。一个健壮的系统需要具备重试能力。为Promise实现重试机制,意味着在操作失败后自动尝试重新执行,直到成功或达到最大重试次数。
![图片[1]-如何实现Promise重试机制?-速码派](http://www.sumapai.com/wp-content/uploads/2026/01/9760b9e796c243deaab2128c6d583400tplv-tb4s082cfz-aigc_resize_1080_1080-1-1024x683.webp)
实现一个基础的重试函数
核心思路是循环执行一个创建Promise的函数。如果成功,则返回结果;如果失败,则等待一段时间后再次尝试,直到重试次数用尽。
function retry(fn, maxAttempts, delay) {
return new Promise((resolve, reject) => {
const attempt = (count) => {
fn().then(resolve).catch(error => {
if (count >= maxAttempts) {
reject(error);
} else {
setTimeout(() => attempt(count + 1), delay);
}
});
};
attempt(1);
});
}
// 使用
const unstableTask = () => fetch('/api/data');
retry(unstableTask, 3, 1000)
.then(data => console.log('成功'))
.catch(err => console.log('重试多次后失败', err));
这个实现会在每次失败后等待固定的延迟时间(1秒)再重试。它简单直接,但缺乏对错误类型的判断和更灵活的退避策略。
使用async/await的递归版本
用async/await和递归实现,逻辑会更清晰,也更容易添加指数退避等高级特性。
async function retryAsync(fn, maxAttempts, baseDelay = 1000) {
let lastError;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
console.log(`尝试 ${attempt} 失败:`, error.message);
if (attempt < maxAttempts) {
const delay = baseDelay * Math.pow(2, attempt - 1); // 指数退避
await new Promise(r => setTimeout(r, delay));
}
}
}
throw lastError;
}
这个版本使用for循环和try...catch。它实现了指数退避策略:每次重试的等待时间是指数增长的(1秒、2秒、4秒…),这有助于减轻服务端的压力。最后抛出的错误是最后一次尝试的失败原因。
考虑错误类型与即时重试
并非所有错误都值得重试。例如,HTTP状态码为400的客户端错误,重试多少次也不会成功。一个更完善的实现应该允许调用者指定重试条件。
async function retryWithCondition(fn, maxAttempts, shouldRetry) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxAttempts || !shouldRetry(error)) {
throw error;
}
console.log(`条件重试中,第 ${attempt} 次`);
}
}
}
// 只对网络错误或5xx状态码重试
retryWithCondition(unstableTask, 3, (error) => {
return error.name === 'TypeError' || error.status >= 500;
});
在实际项目中,重试逻辑需要考虑幂等性(操作重复执行是否安全)、取消机制以及日志记录。对于简单的需求,自己实现一个类似retryAsync的函数就足够了。对于复杂的场景,可以考虑使用p-retry这类经过充分测试的第三方库,它们提供了丰富的配置选项。合理的重试机制能显著提升应用在不可靠网络环境下的韧性。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END






















暂无评论内容