指数退避(Exponential Backoff)在Promise中无法返回数据问题排查
问题排查与修复:429重试时Promise无法返回数据
核心问题原因
当触发429状态码需要重试时,你在res.on('end')回调里直接递归调用了request(options, retries + 1),但这个递归返回的Promise没有被当前Promise的resolve方法处理。这会导致最初创建的Promise一直处于pending状态,永远不会向调用者返回结果。
修复方案
修改res.on('end')中429分支的逻辑,等待递归的request执行完成后,将结果传递给当前Promise的resolve:
async function request(options, retries) { if (retries > 0) { const timeToWait = 15000 * retries; console.log(`waiting for ${timeToWait}ms...`); await waitFor(timeToWait); } return new Promise(async (resolve, reject) => { const req = https.request(options, (res) => { let body = []; res.on('data', (chunk) => { body.push(chunk); }); res.on('end', async () => { try { body = Buffer.concat(body); body = body.toString(); if (body.startsWith('<!DOCTYPE html>')) { _WARN_('Invalid request', 'There was a problem with your request. The parameter(s) you gave are missing or incorrect.'); } else if (body.startsWith('Throttled')) { _WARN_('Throttled request', 'There was a problem with request limit.'); } body = JSON.parse(body); } catch (error) { reject(error); return; }; const returnObject = ReturnObject( !(res.statusCode < 200 || res.statusCode >= 300), res.statusMessage, res.statusCode, body ) if (returnObject.code != 429) { resolve(returnObject); } else { if (retries < maxRetries) { console.log('retrying...'); // 等待递归请求完成,再resolve结果 const retryResult = await request(options, retries + 1); resolve(retryResult); } else { console.log("Max retries reached. Bubbling the error up"); resolve(returnObject); } } }); }); req.on('error', (error) => reject(error)); req.on('timeout', () => { req.abort(); reject(new Error(`Lizard API request timed out. Current timeout is: ${Lizard.TIMEOUT} milliseconds`)); }); req.end(); }); }
关键修改点
- 将外层Promise的回调改为
async函数,同时把res.on('end')的回调也改为async,允许在内部使用await等待递归请求的结果。 - 当需要重试时,用
await获取递归request的返回值,再调用resolve(retryResult)将结果传递给上层Promise。 - 在
catch块中添加return,避免出错后后续逻辑继续执行导致异常。
额外优化建议
- 指数退避通常建议使用指数增长的等待时间(比如
1000 * Math.pow(2, retries)),而非线性增长的15000 * retries,可以减少不必要的等待时长。 - 可以从429响应的
Retry-After头中获取服务器建议的等待时间,替代固定值,更贴合API的限流规则。
内容的提问来源于stack exchange,提问作者alphadmon
相关产品推荐
相关产品推荐

