¿Cómo se estructuran las llamadas de servicio de AWS secuenciales dentro de lambda dado que todas las llamadas son asíncronas?

Vengo de un fondo de Java, así que necesito un poco de novato en las convenciones de JavaScript necesarias para Lambda.

Tengo una función lambda que está destinada a realizar varias tareas de AWS en un orden particular, dependiendo del resultado de la tarea anterior.

Dado que cada tarea informa sus resultados de forma asíncrona, me pregunto si de la manera correcta se asegurará de que todos sucedan en la secuencia correcta, y los resultados de una operación están disponibles para la invocación de la siguiente función.

Parece que tengo que facturar cada función en la callback de la función anterior, pero parece que eso será una especie de anidación profunda y se preguntará si esa es la forma correcta de hacerlo.

Por ejemplo, en una de estas funciones se requiere un objeto de obtención de DynamoDB, seguido de una llamada a SNS para obtener un punto final, seguido de una llamada de SNS para enviar un mensaje, seguido de una escritura de DynamoDB.

¿Cuál es la forma correcta de hacer eso en javascript lambda, teniendo en cuenta toda esa asincronicidad?

Me gusta la respuesta de @jonathanbaraldi, pero creo que sería mejor si administras el flujo de control con Promises. La biblioteca Q tiene algunas funciones prácticas como nbind que ayudan a convertir las API de callback de estilo de nodo como aws-sdk en promesas.

Entonces, en este ejemplo, enviaré un correo electrónico y, tan pronto como la respuesta del correo electrónico vuelva, enviaré un segundo correo electrónico. Esto es esencialmente lo que se pidió, llamando a múltiples servicios en secuencia. Estoy usando el método de promesas para administrar eso de una manera legible verticalmente. También utiliza la catch para manejar los errores. Creo que se lee mucho mejor simplemente anidando funciones de 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 protected]']; var from = '[email protected]'; // 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(); } } 

No sé Lambda, pero debería buscar en la biblioteca de nodos async como una forma de secuenciar funciones asíncronas.

async ha hecho mi vida mucho más fácil y mi código mucho más ordenado sin el problema de anidación profunda que mencionó en su pregunta.

El código asíncrono típico podría verse así:

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

Echa un vistazo al doco async en el enlace de arriba. Hay muchas funciones útiles para serie, paralelo, cascada y muchas más. Async se mantiene activamente y parece muy confiable.

¡buena suerte!

Una solución muy específica que viene a la mente es la conexión en cascada de llamadas Lambda. Por ejemplo, podrías escribir:

  1. Una función Lambda obtiene algo de DynamoDB, y luego invoca …
  2. … una función Lambda que llama a SNS para obtener un punto final y luego invoca …
  3. … una función Lambda que envía un mensaje a través de SNS, luego invoca …
  4. … una función Lambda que escribe en DynamoDB

Todas esas funciones toman la salida de la función anterior como entrada. Por supuesto, esto es muy detallado y puede decidir agrupar ciertas llamadas. Hacerlo de esta manera evita al menos la callbacks en tu código JS.

(Como nota al margen, no estoy seguro de qué tan bien se integra DynamoDB con Lambda. AWS podría emitir eventos de cambio para los registros que luego pueden procesarse a través de Lambda).

Encontré este artículo que parece tener la respuesta en JavaScript nativo.

Cinco patrones para ayudarte a dominar javascript de asynchronis.

Me gustaría ofrecer la siguiente solución, que simplemente crea una estructura de función anidada.

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

Lo que esto hace es copiar todas las variables para la función de llamada que desea realizar, y luego las anida dentro de la llamada anterior. Usted querrá progtwigr sus eventos al revés. Esto es realmente lo mismo que hacer una pirámide de devoluciones de llamada, pero funciona cuando no se sabe de antemano la estructura o la cantidad de llamadas de función. Debe ajustar la función en un cierre para que se copie el valor correcto.

De esta manera, puedo secuenciar las llamadas de servicio de AWS para que vayan 1-2-3 y terminen con el cierre del contexto. Es de suponer que también podría estructurarlo como una stack en lugar de esta pseudo-recursión.

Acabo de ver este hilo viejo. Tenga en cuenta que las futuras versiones de JS mejorarán eso. Eche un vistazo a la syntax async / await de ES2017 que simplifica un desorden de callbacks anidadas asíncronas en un código limpio similar al de la sincronización. Ahora hay algunos polyfills que pueden proporcionarle esta funcionalidad basada en la syntax ES2016.

Como último FYI – AWS Lambda ahora es compatible con .Net Core, que proporciona esta syntax asíncrona limpia desde el primer momento.

Respuesta corta:

Use Async / Await – y llame al servicio de AWS (SNS por ejemplo) con una extensión .promise () para decirle a aws-sdk que use la versión prometida de esa función de servicio en lugar de la versión basada en callback.

Como desea ejecutarlos en un orden específico, puede usar Async / Await suponiendo que la función principal de la que los está llamando es en sí misma async.

Por ejemplo:

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

La parte importante es el .promise () al final allí. Los documentos completos sobre el uso de aws-sdk de forma asíncrona / promesa se pueden encontrar aquí: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html

Para ejecutar otra tarea aws-sdk, de manera similar, agregaría await y la extensión .promise () a esa función (suponiendo que esté disponible).

Para cualquiera que se tope con este hilo y en realidad está buscando simplemente enviar promesas a una matriz y esperar a que se complete TODA la matriz (sin tener en cuenta la promesa que se ejecuta primero), terminé con algo como esto:

 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) 

Espero que ayude a alguien que tropieza al azar en esto a través de Google como lo hice yo!

Por defecto Javascript es asíncrono.

Entonces, todo lo que tienes que hacer, no es usar esas bibliotecas, puedes hacerlo, pero hay formas simples de resolver esto. En este código, envié el correo electrónico con los datos que provienen del evento, pero si lo desea, solo necesita agregar más funciones dentro de las funciones.

Lo que es importante es el lugar donde se encuentra su context.done (); va a ser, él va a terminar su función Lambda. Necesitas ponerlo en el final de la última función.

 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 protected]']; var from = '[email protected]'; // 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(); } }); }