在所有调用都是asynchronous的情况下,如何在lambda中构build连续的AWS服务调用?

我来自java的背景,所以在Lambda所需要的JavaScript惯例上有点新手。

我有一个lambda函数,这意味着要按照特定的顺序执行几个AWS任务,具体取决于前一个任务的结果。

鉴于每个任务都asynchronous报告其结果,我想知道是否正确的方式确保它们都以正确的顺序发生,并且一个操作的结果可用于调用下一个函数。

看起来好像我必须调用前面函数的callback中的每个函数,但是看起来这样会产生某种深层嵌套,并想知道这是否是正确的方法。

举例来说,这些函数需要一个DynamoDB getItem,接着调用SNS来获得一个端点,然后是一个SNS调用来发送消息,然后是DynamoDB写入。

什么是正确的方式来做lambda的JavaScript,占所有的asynchronous性?

我喜欢@jonathanbaraldi的答案,但是我认为如果你用Promise来pipe理控制stream,那会更好。 Q库有一些便利的function,比如nbind ,它帮助将节点式的callbackAPI(如aws-sdk)转换为promise。

因此,在这个例子中,我会发送一封电子邮件,然后一旦电子邮件回复,我会发送第二封电子邮件。 这实质上是所要求的,按顺序调用多个服务。 我正在使用promise的方法来以垂直可读的方式来pipe理它。 还使用catch来处理错误。 我认为它只是简单地嵌套callback函数读得更好。

 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(); } } 

我不知道Lambda,但是您应该查看节点asynchronous库作为对asynchronous函数进行sorting的方法。

asynchronous使我的生活变得更加简单,而且我的代码更加有序,没有您在问题中提到的深层嵌套问题。

典型的asynchronous代码可能如下所示:

 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(); } }); 

看看上面的链接的asynchronousdoco。 串行,并行,瀑布等有很多有用的function。 asynchronous是积极维护,似乎非常可靠。

祝你好运!

想到的一个非常具体的解决scheme是级联Lambda调用。 例如,你可以写:

  1. Lambda函数从DynamoDB获取某些内容,然后调用…
  2. …一个Lambda函数调用SNS来获得一个端点,然后调用…
  3. …通过SNS发送消息的Lambda函数,然后调用…
  4. …写入DynamoDB的Lambda函数

所有这些函数都将前一个函数的输出作为input。 这当然是非常细密的,你可能决定把某些电话分组。 这样做可以避免至less在你的JS代码callback地狱。

(作为一个方面说明,我不确定DynamoDB与Lambda的集成情况如何,AWS可能会发布更改事件以便随后可以通过Lambda进行处理。

我发现这篇文章似乎有原生JavaScript的答案。

五种模式来帮助你驯服asynchronous的JavaScript。

我想提供下面的解决scheme,它只是创build一个嵌套的函数结构。

 // 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); 

这样做是为了复制你想要调用的函数调用的所有variables,然后将它们嵌套在前面的调用中。 你会想要安排你的事件倒退。 这实际上就像制作一个callback金字塔一样,但是当你事先不知道函数调用的结构或者数量的时候,它就起作用了。 您必须将函数包装在闭包中,以便正确的值被复制。

通过这种方式,我可以对AWS服务调用进行sorting,使其进入1-2-3,并以closures上下文结束。 大概你也可以把它构造成一个堆栈而不是这个伪recursion。

刚刚看到这个旧线程。 请注意,JS的未来版本将改善。 看看ES2017asynchronous/等待语法,它将asynchronous嵌套的callback混乱简化为类似代码的干净同步。 现在有一些polyfills可以基于ES2016语法为您提供这种function。

作为最后的FYI,AWS Lambda 现在支持.Net Core ,它提供了这种干净的asynchronous语法。

默认情况下,Javascript是asynchronous的。

所以,你必须做的一切,不是使用这些库,你可以,但有简单的方法来解决这个问题。 在这个代码中,我发送了来自事件的数据的电子邮件,但是如果需要的话,您只需要在函数内添加更多的函数。

重要的是你的context.done(); 将会是,他将要结束你的Lambda函数。 你需要把他放在最后一个function末尾。

 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(); } }); }