如何用非递归方式实现带重试参数的Fetch API请求函数?
把递归的Fetch重试函数改成非递归形式
嘿,这个需求很常见!递归实现重试虽然简单,但改成非递归会让逻辑更直观可控(虽然这里用了setTimeout不会导致栈溢出,但非递归写法可读性更强)。我给你两种实用的非递归方案:
方案一:用Async/Await + 循环(最直观)
这种方式利用async/await的同步写法配合循环,代码非常容易理解:
const fetchRetry = async (url, delay, limit, fetchOptions = {}) => { let attemptCount = 0; while (attemptCount <= limit) { try { const response = await fetch(url, fetchOptions); // 注意:Fetch API只有网络错误才会reject,HTTP错误(如4xx/5xx)会resolve // 这里可以根据你的需求决定是否要把HTTP错误也纳入重试范围 if (!response.ok) { throw new Error(`请求失败,状态码:${response.status}`); } return response; // 成功则直接返回响应 } catch (error) { if (attemptCount === limit) { // 达到重试上限,抛出错误给调用方处理 throw error; } // 等待指定延迟后,进行下一次尝试 await new Promise(resolve => setTimeout(resolve, delay)); attemptCount++; } } };
说明:
- 每次尝试失败后,只要还没到重试次数上限,就等待
delay毫秒后继续循环 - 如果你不需要对HTTP错误进行重试,可以去掉
!response.ok的判断逻辑 - 调用方式和原来一致,返回的仍是Promise:
fetchRetry('/api/data', 1000, 3).then(res => ...).catch(err => ...)
方案二:用Promise链式循环(纯Promise风格)
如果你更习惯纯Promise写法,不想依赖async/await,可以通过循环构建Promise链实现非递归重试:
const fetchRetry = (url, delay, limit, fetchOptions = {}) => { // 初始化一个被拒绝的Promise,触发第一次尝试 let retryPromise = Promise.reject(); for (let i = 0; i <= limit; i++) { retryPromise = retryPromise // 上一次失败后,发起新的请求 .catch(() => fetch(url, fetchOptions)) .then(response => { if (!response.ok) { throw new Error(`请求失败,状态码:${response.status}`); } return response; }) .catch(error => { // 如果是最后一次尝试失败,直接抛出错误 if (i === limit) { throw error; } // 否则等待延迟后,让Promise链继续执行下一次尝试 return new Promise(resolve => setTimeout(resolve, delay)); }); } return retryPromise; };
说明:
- 通过循环逐步构建Promise链,每一轮循环对应一次重试尝试
- 同样处理了HTTP错误的情况,你可以根据需求调整
- 这种方式不需要依赖
async/await,适合一些对语法有限制的环境
这两种方案都完全避免了递归调用,逻辑清晰且容易维护,你可以根据自己的代码风格选择合适的版本~
内容的提问来源于stack exchange,提问作者Andy
相关产品推荐
相关产品推荐

