AWS Lambda无法执行多顺序Node.js函数问题求助
AWS Lambda 多顺序函数执行问题修复
我正尝试将index.js文件部署到AWS Lambda,实现每日凌晨自动执行。已完成文件上传操作,但代码在Lambda环境中无法按顺序执行多个函数:本地执行node index.js一切正常,但Lambda测试时仅第一个getToken函数触发。需要调整代码,确保先获取API访问令牌,再用令牌更新DynamoDB表。
原代码:
var AWS = require('aws-sdk'); AWS.config.update({region: 'us-west-2'}); async function getToken (event, context) { artistRank = 0; console.log("GETTING TOKEN...") getToken = await fetch("https://api.chartmetric.com/api/token", { body: '{"refreshtoken":"*************************************"}', headers: { "Content-Type": "application/json" }, method: "POST" }) .then( res => res.json() ) .then( data => { token = data.token; console.log(token) readTable(token); })}; async function readTable (token) { // Create the DynamoDB service object var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'}); for (let i = 0; i < 50; i++) { setTimeout(() => { var params = { TableName: 'popularCharts', Key: { 'rank': {N: String(i)} }, ProjectionExpression: 'currentRank, id' }; ddb.getItem(params, function (err, data) { if (err) { console.log("Error", err); } else { artistId = data['Item'].id idString = JSON.stringify(artistId); idLength = idString.length; idFinal = idString.slice(6, idLength - 2); pointsScored(token, idFinal, i) } }); }, i*2000); } } async function pointsScored (accessKey, id, rank) { fetch("https://api.chartmetric.com/api/artist/" + id + "/cpp?stat=score", { headers: { Authorization: "Bearer " + String(accessKey) } }) .then( res => res.json() ) .then( data => { length = data['obj'].length; change = (data['obj'][length-1].score - data['obj'][length-4].score)*1000000; console.log(change) var ddb2 = new AWS.DynamoDB({apiVersion: '2012-08-10'}); var params2 = { TableName: 'pointsScored', Item: { 'rank' : {N: String(rank)}, 'id' : {N: String(id)}, 'playlistPoints' : {S: String(change)}, 'currentRank' : {N: String(rank)} } } ddb2.putItem(params2, function(err, data) { if (err) { console.log("Error", err);} }); }) } exports.handler = getToken();
问题根源及修复方案
1. Lambda Handler配置错误
原代码直接调用getToken()并赋值给exports.handler,会在代码加载时立即执行函数,而非由Lambda触发时执行。正确做法是导出函数本身,且需确保handler等待所有异步操作完成,避免Lambda提前终止。
2. 未处理异步操作的等待
Lambda会在事件循环为空时终止执行,原代码中readTable和pointsScored里的setTimeout、回调式DynamoDB操作都是异步的,未被等待,导致Lambda在这些操作完成前就结束了。
3. 全局变量风险
代码中使用了artistRank、token等未声明的全局变量,易引发意外的变量污染和逻辑错误,需改为局部变量并使用let/const声明。
4. Await与回调混用
getToken中同时使用await和.then(),逻辑混乱,应统一使用await处理异步操作。
修正后的完整代码
const AWS = require('aws-sdk'); AWS.config.update({ region: 'us-west-2' }); // 工具函数:实现延迟 const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); async function getToken(event, context) { let artistRank = 0; console.log("GETTING TOKEN..."); const tokenResponse = await fetch("https://api.chartmetric.com/api/token", { body: '{"refreshtoken":"*************************************"}', headers: { "Content-Type": "application/json" }, method: "POST" }); const data = await tokenResponse.json(); const token = data.token; console.log(token); // 等待readTable完成所有操作 await readTable(token); } async function readTable(token) { const ddb = new AWS.DynamoDB({ apiVersion: '2012-08-10' }); // 改用异步循环,确保每个请求完成后再进行下一个,同时遵守速率限制 for (let i = 0; i < 50; i++) { const params = { TableName: 'popularCharts', Key: { 'rank': { N: String(i) } }, ProjectionExpression: 'currentRank, id' }; try { const data = await ddb.getItem(params).promise(); const artistId = data.Item.id; // 简化ID提取逻辑 const idFinal = artistId.N; // 直接获取DynamoDB数字类型的原始值 // 等待pointsScored完成 await pointsScored(token, idFinal, i); } catch (err) { console.log("Error reading item:", err); } // 每次请求后延迟2秒,避免API限流 if (i < 49) { await delay(2000); } } } async function pointsScored(accessKey, id, rank) { const response = await fetch(`https://api.chartmetric.com/api/artist/${id}/cpp?stat=score`, { headers: { Authorization: `Bearer ${accessKey}` } }); const data = await response.json(); const length = data.obj.length; const change = (data.obj[length - 1].score - data.obj[length - 4].score) * 1000000; console.log(`Rank ${rank} change: ${change}`); const ddb2 = new AWS.DynamoDB({ apiVersion: '2012-08-10' }); const params2 = { TableName: 'pointsScored', Item: { 'rank': { N: String(rank) }, 'id': { N: id }, 'playlistPoints': { S: String(change) }, 'currentRank': { N: String(rank) } } }; try { await ddb2.putItem(params2).promise(); } catch (err) { console.log("Error putting item:", err); } } // 正确导出Lambda handler exports.handler = getToken;
关键修正说明
- Handler修正:直接导出
getToken函数,Lambda会在触发时传入event和context参数,且getToken是异步函数,会等待后续所有操作完成。 - 统一使用Await:所有异步操作(fetch、DynamoDB调用)都改用
await处理,确保执行顺序和完成等待。 - 全局变量替换:所有变量改为局部变量,用
let/const声明,避免污染。 - 简化ID提取:直接从DynamoDB返回的Item中获取
id.N,无需JSON.stringify后截取字符串。 - 异步循环:将原
setTimeout循环改为带延迟的异步循环,确保每个请求依次执行,同时避免API限流。 - 错误处理:添加
try/catch捕获异步操作中的错误,便于排查问题。
内容的提问来源于stack exchange,提问作者Jack Eggert
相关产品推荐
相关产品推荐

