在开发网页应用时,经常会遇到网络波动导致Ajax请求失败的情况。比如用户在地铁里刷页面,突然进入隧道,请求发不出去;或者服务器短暂卡顿,返回502错误。这时候如果能让请求自动重试,体验会好很多。
为什么需要自动重连
想象一下,用户提交了一个订单,点击后页面没反应,刷新一下又发现订单已经生成了。这种情况很可能就是请求发送成功了,但响应没收到,前端没得到反馈。如果请求能自动重连几次,大概率就能拿到结果,避免重复提交。
基本思路:封装Ajax并加入重试机制
我们可以用原生fetch或jQuery的$.ajax,在外层封装一个带重试逻辑的函数。核心就是监听请求失败,然后在一定延迟后重新发起,直到成功或达到最大重试次数。
function ajaxWithRetry(url, options = {}, maxRetries = 3, delay = 1000) {
return new Promise((resolve, reject) => {
function attempt(retryCount) {
fetch(url, options)
.then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then(resolve)
.catch(error => {
if (retryCount >= maxRetries) {
reject(error);
} else {
setTimeout(() => {
console.log(`请求失败,${delay}ms后重试(第${retryCount}次)`);
attempt(retryCount + 1);
}, delay);
}
});
}
attempt(1);
});
}
实际使用示例
比如我们要获取用户信息,在弱网环境下希望最多重试两次:
ajaxWithRetry('/api/user/profile', {
method: 'GET'
}, 2, 1500)
.then(data => {
console.log('用户数据:', data);
})
.catch(err => {
console.error('最终失败:', err.message);
});
进阶优化:指数退避
连续重试间隔固定时间可能不太友好。可以改成指数增长,比如第一次等1秒,第二次等2秒,第三次等4秒,减轻服务器压力。
setTimeout(() => {
attempt(retryCount + 1);
}, delay * Math.pow(2, retryCount - 1));
只对特定错误重试
不是所有失败都值得重试。比如404找不到接口,再试也没用。应该只对网络错误、5xx服务器问题或超时做重连。
.catch(error => {
const networkError = ['Failed to fetch', 'NetworkError'].some(msg =>
error.message.includes(msg)
);
const serverError = error.message.includes('HTTP 5');
if (!(networkError || serverError)) {
return reject(error); // 不重试,直接抛出
}
if (retryCount >= maxRetries) {
reject(error);
} else {
// 继续重试...
}
});
结合业务场景灵活控制
有些操作不能重复提交,比如支付请求。这时候可以在封装时加个标记,确保即使重连也只发一次。而轮询类请求,比如查订单状态,反而可以主动多试几次。
把重连逻辑抽成工具函数后,项目里所有关键请求都可以统一加上这层保护,既不影响原有代码结构,又能显著提升稳定性。