17

I'm coming from a java background so a bit of a newbie on Javascript conventions needed for Lambda.

I've got a lambda function which is meant to do several AWS tasks in a particular order, depending on the result of the previous task.

Given that each task reports its results asynchronously, I'm wondering if the right way make sure they all happen in the right sequence, and the results of one operation are available to the invocation of the next function.

It seems like I have to invoike each function in the callback of the prior function, but seems like that will some kind of deep nesting and wondering if that is the proper way to do this.

For example on of these functions requires a DynamoDB getItem, following by a call to SNS to get an endpoint, followed by a SNS call to send a message, followed by a DynamoDB write.

What's the right way to do that in lambda javascript, accounting for all that asynchronicity?

4

8 回答 8

4

我喜欢@jonathanbaraldi 的回答,但我认为如果您使用 Promises 管理控制流会更好。Q 库有一些方便的函数nbind,比如帮助将节点样式回调 API(如 aws-sdk)转换为 Promise。

因此,在本例中,我将发送一封电子邮件,然后在收到电子邮件回复后,我将发送第二封电子邮件。这基本上就是要求的,依次调用多个服务。我正在使用thenpromises 的方法以垂直可读的方式来管理它。也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();
    }
}
于 2015-03-20T08:22:48.910 回答
3

简短的回答:

使用 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() 扩展添加到该函数(假设可用)。

对于遇到此线程并且实际上希望简单地将承诺推送到数组并等待整个数组完成(不考虑首先执行哪个承诺)的任何人,我最终得到了这样的结果:

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)

希望能帮助像我一样通过谷歌随机偶然发现的人!

于 2018-07-12T20:35:01.767 回答
2

我不了解 Lambda,但您应该查看节点异步库作为对异步函数进行排序的一种方式。

没有您在问题中提到的深层嵌套问题,异步使我的生活变得更加轻松,我的代码更加有序。

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

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

在上面的链接中查看 async doco。串行、并行、瀑布流等有许多有用的功能。Async 得到积极维护,看起来非常可靠。

祝你好运!

于 2014-12-31T06:31:11.590 回答
1

刚看到这个老贴。请注意,未来的 JS 版本会改进这一点。看看ES2017 async/await语法,它将异步嵌套回调混乱简化为干净的同步代码。现在有一些polyfill可以基于 ES2016 语法为您提供此功能。

作为最后一个仅供参考 - AWS Lambda现在支持 .Net Core,它提供了开箱即用的这种干净的异步语法。

于 2017-01-24T20:31:02.527 回答
1

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

  1. 一个 Lambda 函数从 DynamoDB 中获取一些东西,然后调用……</li>
  2. ......一个调用 SNS 以获取端点,然后调用......的 Lambda 函数......</li>
  3. …一个 Lambda 函数,通过 SNS 发送消息,然后调用…</li>
  4. …写入 DynamoDB 的 Lambda 函数

所有这些函数都将前一个函数的输出作为输入。这当然是非常细粒度的,您可能会决定对某些呼叫进行分组。这样做至少可以避免 JS 代码中的回调地狱。

(附带说明一下,我不确定 DynamoDB 与 Lambda 的集成情况如何。AWS 可能会为可以通过 Lambda 处理的记录发出更改事件。)

于 2015-10-06T07:42:52.360 回答
0

我发现这篇文章似乎在本机 javascript 中有答案。

五种模式可帮助您驯服异步 javascript。

于 2014-12-31T08:25:02.310 回答
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 结束,并以关闭上下文结束。大概您也可以将其构造为堆栈而不是这种伪递归。

于 2015-09-16T21:30:22.267 回答
-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();
          }
     });
}
于 2015-02-18T04:00:08.660 回答