AWS Lambda环境下异步函数15秒重试超时失效问题求助
问题:Vercel(AWS Lambda)环境下异步重试的超时控制失效
本地运行正常的15秒超时重试逻辑,部署到Vercel(基于AWS Lambda)后,setTimeout被完全忽略,fetchData函数会持续运行直到获取正确响应或触发服务器默认60秒超时。
原代码如下:
router.get('/test', async (req, res) => { try { const browser = await playwright.launchChromium({ headless: false }); const context = await browser.newContext(); const page = await context.newPage(); let timeout = false; let fetchDataTimeout = setTimeout(() => { timeout = true; }, 15000); const fetchData = async () => { await page.type('#code', '6824498040'); let data = await ( await Promise.all([ page.waitForResponse( (response) => response.url() === `${env.API_URL2}` && response.status() === 200, { timeout: 10000 }, ), page.click('#btn-check'), ]) )[0].json(); if (data.errors && !timeout) { await page.reload(); return await fetchData(); } else { clearTimeout(fetchDataTimeout); return data; } }; let data = await fetchData(); await browser.close(); res.json({ status: 200, message: data, }); } catch (error) { console.error(error); return res.status(500).send({ 'Server Error': `${error}` }); } });
解决方案:用Promise.race实现可取消的超时控制
AWS Lambda的执行环境对同步/异步逻辑的处理和本地Node.js存在差异,直接用setTimeout标记全局变量的方式不可靠。正确的做法是把超时逻辑包装成带取消功能的Promise,通过Promise.race让重试逻辑和超时逻辑竞争,同时支持在成功获取数据后终止超时流程。
修改后的代码如下:
router.get('/test', async (req, res) => { try { const browser = await playwright.launchChromium({ headless: false }); const context = await browser.newContext(); const page = await context.newPage(); // 封装带取消功能的超时Promise const createTimeoutPromise = () => { let timeoutId; const promise = new Promise((_, reject) => { timeoutId = setTimeout(() => { reject(new Error('请求超时,已停止重试')); }, 15000); }); return { promise, cancel: () => clearTimeout(timeoutId) }; }; const { promise: timeoutPromise, cancel: cancelTimeout } = createTimeoutPromise(); // 重试逻辑封装 const fetchDataWithRetry = async () => { await page.type('#code', '6824498040'); const response = await Promise.all([ page.waitForResponse( (res) => res.url() === `${env.API_URL2}` && res.status() === 200, { timeout: 10000 } ), page.click('#btn-check') ])[0]; const data = await response.json(); if (data.errors) { await page.reload(); return fetchDataWithRetry(); } return data; }; // 让重试逻辑与超时逻辑竞争执行 const data = await Promise.race([ fetchDataWithRetry(), timeoutPromise ]); // 成功获取数据,立即取消超时定时器 cancelTimeout(); await browser.close(); res.json({ status: 200, message: data }); } catch (error) { console.error(error); // 确保浏览器关闭,避免资源泄漏 if (typeof browser !== 'undefined') { await browser.close(); } return res.status(500).send({ 'Server Error': error.message }); } });
关键改动说明:
- 带取消的超时Promise:通过封装返回Promise和取消方法,成功获取数据后调用
cancelTimeout清除定时器,避免不必要的超时触发。 - Promise.race竞争机制:让重试逻辑和超时逻辑同时执行,哪个先完成就取哪个结果——超时先到则抛出错误终止流程,成功获取数据则返回结果并取消超时。
- 移除全局timeout变量:不再依赖外部变量控制重试,完全通过Promise状态和递归逻辑实现,避免Lambda环境下的变量状态异常。
内容的提问来源于stack exchange,提问作者lenny73
相关产品推荐
相关产品推荐

