我来自java背景,所以对于Lambda来说,需要一些关于Javascript约定的新手。
我有一个lambda函数,用于按特定顺序执行多个AWS任务,具体取决于上一个任务的结果。
鉴于每个任务都异步报告其结果,我想知道是否正确的方法确保它们都以正确的顺序发生,并且一个操作的结果可用于调用下一个函数。
似乎我必须在前一个函数的回调函数中调用每个函数,但似乎会有某种深度嵌套,并且想知道这是否是正确的方法。
例如,这些函数需要一个DynamoDB getItem,然后调用SNS获取一个端点,接着是一个SNS调用来发送消息,然后是DynamoDB写入。
在lambda javascript中正确的方法是什么,考虑到所有的异步性?
答案 0 :(得分:4)
我喜欢@jonathanbaraldi的回答,但我认为如果你使用Promise管理控制流会更好。 Q库有一些方便的功能,比如nbind
,它有助于将节点样式的回调API转换为承诺,就像aws-sdk一样。
因此,在此示例中,我将发送一封电子邮件,然后一旦电子邮件回复,我将发送第二封电子邮件。这基本上就是所要求的,按顺序调用多个服务。我使用then
promises方法以垂直可读的方式管理它。还使用catch
来处理错误。我认为只是简单地嵌套回调函数就会好得多。
var Q = require('q');
var AWS = require('aws-sdk');
AWS.config.credentials = { "accessKeyId": "AAAA","secretAccessKey": "BBBB"};
AWS.config.region = 'us-east-1';
// Use a promised version of sendEmail
var ses = new AWS.SES({apiVersion: '2010-12-01'});
var sendEmail = Q.nbind(ses.sendEmail, ses);
exports.handler = function(event, context) {
console.log(event.nome);
console.log(event.email);
console.log(event.mensagem);
var nome = event.nome;
var email = event.email;
var mensagem = event.mensagem;
var to = ['email@company.com.br'];
var from = 'site@company.com.br';
// Send email
mensagem = ""+nome+"||"+email+"||"+mensagem+"";
console.log(mensagem);
var params = {
Source: from,
Destination: { ToAddresses: to },
Message: {
Subject: {
Data: 'Form contact our Site'
},
Body: {
Text: {
Data: mensagem,
}
}
};
// Here is the white-meat of the program right here.
sendEmail(params)
.then(sendAnotherEmail)
.then(success)
.catch(logErrors);
function sendAnotherEmail(data) {
console.log("FIRST EMAIL SENT="+data);
// send a second one.
return sendEmail(params);
}
function logErrors(err) {
console.log("ERROR="+err, err.stack);
context.done();
}
function success(data) {
console.log("SECOND EMAIL SENT="+data);
context.done();
}
}
答案 1 :(得分:2)
我不知道Lambda,但您应该将节点async library作为对异步函数进行排序的方法。
async让我的生活变得更轻松,而且我的代码更加有序,没有您在问题中提到的深层嵌套问题。
典型的异步代码可能如下所示:
async.waterfall([
function doTheFirstThing(callback) {
db.somecollection.find({}).toArray(callback);
},
function useresult(dbFindResult, callback) {
do some other stuff (could be synch or async)
etc etc etc
callback(null);
],
function (err) {
//this last function runs anytime any callback has an error, or if no error
// then when the last function in the array above invokes callback.
if (err) { sendForTheCodeDoctor(); }
});
在上面的链接中查看异步doco。串行,并行,瀑布等等有许多有用的功能。异步被积极维护并且看起来非常可靠。
祝你好运!答案 2 :(得分:1)
想到一个非常具体的解决方案是级联Lambda调用。例如,您可以写:
所有这些函数都将前一个函数的输出作为输入。这当然是非常细粒度的,您可能决定对某些呼叫进行分组。这样做至少可以避免JS代码中的回调地狱。
(作为旁注,我不确定DynamoDB与Lambda的集成程度如何.AWS可能会为记录发出更改事件,然后可以通过Lambda处理。)
答案 3 :(得分:1)
简短回答:
使用Async / Await —并使用扩展名.promise()调用AWS服务(例如SNS),以告诉aws-sdk使用该服务功能的承诺版本而不是基于回叫的版本。
由于您要按特定顺序执行它们,因此可以使用Async / Await,前提是您要从中调用它们的父函数本身是异步的。
例如:
let snsResult = await sns.publish({
Message: snsPayload,
MessageStructure: 'json',
TargetArn: endPointArn
}, async function (err, data) {
if (err) {
console.log("SNS Push Failed:");
console.log(err.stack);
return;
}
console.log('SNS push suceeded: ' + data);
return data;
}).promise();
重要的是最后的.promise()。有关以异步/基于承诺的方式使用aws-sdk的完整文档,请参见:https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
要运行另一个aws-sdk任务,您可以类似地向该函数添加await和.promise()扩展名(假设可用)。 >
对于遇到此线程并实际上只是希望将promise推入数组并等待该WHOLE数组完成(不考虑promise首先执行)的任何人,我都会得到以下结果:
let snsPromises = [] // declare array to hold promises
let snsResult = await sns.publish({
Message: snsPayload,
MessageStructure: 'json',
TargetArn: endPointArn
}, async function (err, data) {
if (err) {
console.log("Search Push Failed:");
console.log(err.stack);
return;
}
console.log('Search push suceeded: ' + data);
return data;
}).promise();
snsPromises.push(snsResult)
await Promise.all(snsPromises)
希望能像我一样帮助通过Google偶然发现这一问题的人!
答案 4 :(得分:0)
我发现这篇文章似乎在原生javascript中有答案。
答案 5 :(得分:0)
我想提供以下解决方案,它只是创建一个嵌套的函数结构。
// start with the last action
var next = function() { context.succeed(); };
// for every new function, pass it the old one
next = (function(param1, param2, next) {
return function() { serviceCall(param1, param2, next); };
})("x", "y", next);
这样做的目的是复制你想要进行的函数调用的所有变量,然后将它们嵌入上一次调用中。您想要向后安排活动。这实际上与制作金字塔回调相同,但是当你事先不知道函数调用的结构或数量时,它就会起作用。您必须将函数包装在闭包中,以便复制正确的值。
通过这种方式,我能够对AWS服务调用进行排序,使得它们以1-2-3结束并以关闭上下文结束。据推测,您也可以将其构造为堆栈而不是伪递归。
答案 6 :(得分:0)
刚刚看到这个旧线程。请注意,JS的未来版本将改进这一点。看看ES2017 async/await语法,它将异步嵌套回调混乱简化为类似代码的干净同步。 现在有一些polyfills可以根据ES2016语法为您提供此功能。
作为最后一个FYI - AWS Lambda now supports .Net Core,提供开箱即用的干净异步语法。
答案 7 :(得分:-2)
默认情况下,Javascript是异步的。
所以,你必须做的一切,不是使用这些库,你可以,但有简单的方法来解决这个问题。在这段代码中,我发送了包含事件数据的电子邮件,但如果您愿意,您只需要在函数内添加更多函数。
重要的是context.done();将会是,他将结束你的Lambda功能。你需要把他放在最后一个函数的末尾。
var AWS = require('aws-sdk');
AWS.config.credentials = { "accessKeyId": "AAAA","secretAccessKey": "BBBB"};
AWS.config.region = 'us-east-1';
var ses = new AWS.SES({apiVersion: '2010-12-01'});
exports.handler = function(event, context) {
console.log(event.nome);
console.log(event.email);
console.log(event.mensagem);
nome = event.nome;
email = event.email;
mensagem = event.mensagem;
var to = ['email@company.com.br'];
var from = 'site@company.com.br';
// Send email
mensagem = ""+nome+"||"+email+"||"+mensagem+"";
console.log(mensagem);
ses.sendEmail( {
Source: from,
Destination: { ToAddresses: to },
Message: {
Subject: {
Data: 'Form contact our Site'
},
Body: {
Text: {
Data: mensagem,
}
}
}
},
function(err, data) {
if (err) {
console.log("ERROR="+err, err.stack);
context.done();
} else {
console.log("EMAIL SENT="+data);
context.done();
}
});
}