如何在原生JavaScript中实现支持取消与错误码判断的Promise重试函数?
带重试、取消机制和HTTP错误码区分的API请求工具函数
针对你需要的具备健壮性的API请求重试函数,以下是优化后的实现,同时解决取消机制和HTTP错误码区分处理两个核心问题:
function retry(fn, retries = 3, delay = 1000) { // 用于取消重试的控制器 const controller = new AbortController(); const signal = controller.signal; const promise = new Promise((resolve, reject) => { let timeoutId; const attempt = (n) => { // 如果已取消,直接终止 if (signal.aborted) { reject(new Error("请求已取消")); return; } fn(signal) .then((response) => { // 处理HTTP状态码:4xx直接拒绝,不重试 if (!response.ok) { if (response.status >= 400 && response.status < 500) { reject(new Error(`HTTP错误:${response.status} ${response.statusText}`)); return; } // 5xx错误,进入重试逻辑 throw new Error(`服务器错误:${response.status} ${response.statusText}`); } resolve(response); }) .catch((error) => { // 如果是取消错误,直接拒绝 if (error.name === "AbortError") { reject(error); return; } // 剩余重试次数为0时,拒绝 if (n === 0) { reject(error); } else { // 延迟后重试 timeoutId = setTimeout(() => { attempt(n - 1); }, delay); } }); }; // 监听取消信号,清理定时器 signal.addEventListener("abort", () => { if (timeoutId) clearTimeout(timeoutId); }); attempt(retries); }); // 给Promise添加cancel方法,外部可调用取消 promise.cancel = () => { controller.abort(); }; return promise; } // 测试用例 const fetchWithRetry = () => { return retry((signal) => fetch("https://jsonplaceholder.typicode.com/todos/95", { signal }) ); }; // 使用示例 const request = fetchWithRetry(); request .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('请求失败:', error)); // 模拟用户取消(比如页面跳转/关闭时调用) // setTimeout(() => { // request.cancel(); // }, 1500);
核心功能说明
取消机制实现:
- 借助
AbortController和AbortSignal控制请求生命周期,为返回的Promise新增cancel方法,外部可主动触发取消操作 - 监听
abort信号,清理待执行的重试定时器,避免内存泄漏 - 触发取消后,直接终止后续重试流程并抛出取消错误
- 借助
HTTP错误码区分处理:
- 请求成功后先校验
response.ok,对非2xx状态码分类处理 - 4xx客户端错误(如401、403)直接拒绝,不进入重试逻辑
- 5xx服务器错误抛出异常,触发重试流程
- 网络错误或其他异常同样触发重试,直到耗尽重试次数
- 请求成功后先校验
使用注意事项
- 传入的请求函数(如
fetch)需要支持接收signal参数,并绑定到请求配置中,才能在取消时终止正在进行的请求 - 可根据业务需求调整重试次数
retries和延迟时间delay - 在页面卸载、路由跳转等场景下,调用返回Promise的
cancel()方法即可终止所有待重试请求
内容的提问来源于stack exchange,提问作者dram95
相关产品推荐
相关产品推荐

