递归函数中如何触发原始Promise的resolve?Lambda分页API调用异常问题
问题根因
你代码的核心问题是只有最后一页的api_get返回的Promise被resolve了,前面所有分页对应的Promise实例都从未触发resolve,导致最外层的await api_get('users')永远处于等待状态,自然不会执行后续的"Function finished"日志。
你可以这么理解:每调用一次api_get就会创建一个新的Promise实例。当拉取第1页发现还有下一页时,你只是await了第2页的api_get,但是第1页自己的Promise的resolve()方法从来没有被调用过;同理第2页、第N-1页的Promise都没有被resolve,只有最后一页走到了else分支调用了resolve,外层等待的是第1页的Promise,它永远处于pending状态,所以代码卡住。
修复方案
你只需要在递归调用完下一页的api_get之后,把当前页的Promise也resolve掉即可,改动很小:
exports.handler = async (event, context, callback) => { let db = await initDB(); // External function try { await api_get('users') console.log("Function finished"); } catch (err) { console.error('执行出错', err) } finally { db.end() callback(null) } function api_get(type, page = 1, count = 0, results = []) { return new Promise(function(resolve, reject) { // 不要给Promise构造函数的入参加async,无意义还容易引发上下文错误 let uri = `https://api.com/v1/${type}`; request.get(uri + '?page=' + page, async (error, response, body) => { // 新增错误处理,避免请求出错时Promise永久pending if (error) return reject(error) try { const data = JSON.parse(body); results.push(data.users) const current_count = count + data.meta.count if (current_count < data.meta.total) { await api_get(type, page + 1, current_count, results); // 递归完成后直接resolve当前页的Promise resolve() } else { await parseData(type, results) resolve() } } catch (parseErr) { reject(parseErr) } }); }); } function parseData(type, data) { return new Promise(function(resolve, reject) { switch (type) { case 'users': let users = [].concat(...data) // 注意:原代码中的user_id变量不存在,需根据实际字段改为user.user_id/user.id // 注意:直接拼接SQL存在严重SQL注入风险,建议改为参数化查询 let sql = '' for (const user of users) { sql = sql + `INSERT INTO users (user_id) VALUES ('${user.user_id}');` } db.query(sql) .then(() => resolve()) .catch(sqlErr => reject(sqlErr)) break; default: resolve() } }); } };
其他优化建议
- 停止使用已废弃的
request库,替换为axios、got等仍在维护的HTTP请求库 - 所有异步操作统一添加try/catch捕获,避免Lambda无意义超时
- 数据库写入操作改用参数化查询,完全避免SQL注入风险
内容的提问来源于stack exchange,提问作者Andrew White
相关产品推荐
相关产品推荐

