做数据备份时,网络请求失败是常有的事。比如你设置好了定时同步,结果公司网络突然卡了一下,上传中断,备份没成功。这时候,光靠手动重来一遍太麻烦,得让程序自己知道“失败了就再试几次”。
为什么需要重试机制
网络不稳定、服务器临时过载、DNS 解析超时,这些都可能导致一次请求失败。但很多情况下,几秒后重试就能成功。比如你在咖啡馆连 Wi-Fi 备份照片,信号波动导致上传中断,如果程序能自动重试,你就不用盯着进度条手动点“重试”了。
简单的重试逻辑怎么写
最基础的做法是用循环加延迟。比如发送一个 HTTP 请求,失败后等两秒再试,最多试三次。
function fetchDataWithRetry(url, retries = 3, delay = 2000) {
return new Promise((resolve, reject) => {
const attempt = (count) => {
fetch(url)
.then(resolve)
.catch((err) => {
if (count >= retries) {
reject(err);
} else {
setTimeout(() => {
console.log(`请求失败,${delay/1000}秒后重试,剩余次数: ${retries - count}`);
attempt(count + 1);
}, delay);
}
});
};
attempt(1);
});
}
调用这个函数,就算第一次网络抖动,它也会自动再试两次。
别盲目重试
不是所有失败都值得重试。比如返回 404 是资源不存在,再试十次也没用;而 503 服务暂时不可用,重试就有意义。应该根据错误类型决定是否重试。
const shouldRetry = (error) => {
if (error.status) {
return [500, 502, 503, 504].includes(error.status);
}
// 网络断开、超时也重试
return error.name === 'TypeError' || error.name === 'TimeoutError';
};
加入指数退避更聪明
连续重试间隔太短,可能让服务器雪上加霜。更好的方式是“越往后等越久”,比如第一次等 2 秒,第二次 4 秒,第三次 8 秒。
function exponentialBackoff(retryCount) {
const delay = Math.pow(2, retryCount) * 1000; // 2^count 秒
return delay;
}
结合前面的逻辑,每次重试的等待时间翻倍,既给了系统恢复时间,又避免了密集冲击。
实际应用场景
比如你写的备份脚本要往云存储上传文件,网络请求一旦失败,可以自动触发重试。配合日志记录,还能知道哪次传失败了、重试了几次才成功,方便排查问题。
有些现成的库也能帮你处理,比如 Axios 的拦截器加上重试插件,或者用 got、ky 这类支持原生重试的 HTTP 客户端,省得从零造轮子。
关键是在不稳定环境中保持任务的韧性。数据备份不是一锤子买卖,中间出点岔子,程序得能自己爬起来继续干。