AWS Lambda doesn't work with `async`, Only with callbacks? - node.js

I have a Lambda function in AWS using Node 8.1 which initiate MSSQL request ( to an external server , which is not in aws)
When I'm using the non async handler with non async code (but callbacks )—
exports.handler = (event, context, callback) => {...}
— Everything is OK.
Proof: For this code, which uses callbacks-
exports.handler = (event, context, callback) => {
sql.connect(config, err => {
if (err) {
callback(err);
} else {
const req = new sql.Request();
req.query(
'select * from img.dbo.images where imageid = 1641',
(error, result) => {
if (error) {
callback(error);
} else {
sql.close();
callback(null, result.recordset);
}
}
);
}
});
};
I get this response :
However , If I change the code to the async version :
exports.handler = async (event, context, callback) => {
try {
let pool = await sql.connect(config);
let result1 = await pool
.request()
.query('select * from img.dbo.images where imageid = 1641');
callback(null, result1.recordset);
} catch (err) {
callback(err);
}
};
— I get a Timeout error(sure it's related to a non-fulfill promise) :
Question
Why doesn't the async version work ? how can I make it work?

Remove the callback param and just return the result instead. Also, you can avoid catching errors unless you need to do any special handling
exports.handler = async (event, context) => {
const pool = await sql.connect(config);
return await pool
.request()
.query('select * from img.dbo.images where imageid = 1641');
}

Related

Await Promise.all async does't work in my code

I have a lambda function with the structure below,
It used to work in older versions of nodejs but it doesn't work with the newer versions.
I know my code structure is quite messy and wrong but I can't get my head around it. I'm trying to use Promise.all but I'm obviously doing something wrong cause it's not getting executed at all.
By the way, I'm not getting any errors. The promise.all method never gets executed.
let AWS = require('aws-sdk');
exports.handler = async(event, context, callback) => {
let result = {};
try {
result = await getOrder(sql, 0);
result.map(
(dataField) => {
});
}
catch (error) {
console.log(error);
callback(error);
}
var today_result = [];
const groupKey = i => i.user_id + '_' + i.when;
const counts = _.countBy(followingsIDs, groupKey);
const isMulti = i => counts[groupKey(i)] > 1;
const multiPropkey = i => ({ multiplekey: isMulti(i) ? groupKey(i) : groupKey(i) });
const multiProp = i => ({ multiple: isMulti(i) ? counts[groupKey(i)] : 1 });
const updated = _.map(followingsIDs, i => _.extend(i, multiProp(i), multiPropkey(i)));
const uniqResult = _.uniq(updated, function(d) { return d.multiplekey });
// Doesn’t execute from here —>
await Promise.all(uniqResult.map(async(dataField) => {
console.log("test_");
dosomething()
if (true) {
let sql = `INSERT INTO ….`
result = await getOrder(sql, 0);
try {
const data = await sns.publish(params).promise();
}
catch (e) {
console.log(e.stack);
response.result = 'Error';
}
}
}));
// Till here <----
callback(null, uniqResult);
};
let getOrder = async(sql, params) => {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) throw err;
connection.query(sql, params, (err, results) => {
if (err) {
reject(err);
}
// console.log("-----Query Done!");
connection.release();
// console.log("-----Data: ", results);
resolve(results);
});
});
});
};
What are you awaiting to? The uniqResult is just declared as an empty array. Immediately after that you pass it to Promise.all. You need to fill it with Promises and then pass it to Promise.all.

Aws lambda return promise

I am working with aws lamda and creating a function returning a promise from it and then consuming it in my client function in node. Due to some reason it is returning Promise and i am not able to figure out what is wrong here. Here is the following code snippet : -
Lamda Function
function paymentConfirmationMessage() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("I came from lamda");
}, 20000);
});
}
exports.handler = async (event) => {
let calculationResult = await paymentConfirmationMessage();
return calculationResult;
}
In node client : -
const https = require("https");
const url = "SomeLamdaurl.com/get";
function getData(url) {
return new Promise(resolve => {
https.get(url, function (error, response, body) {
resolve(body);
});
});
}
async function test(){
var body = await getData(url);
if (body) {
const obj = JSON.parse(body);
console.log('obj', obj);
}
else {
}
}
test();
Additionally when i am making call from api gateway setup in lamda i am getting the desired response.
Any lead will be helpful. Thanks!

Listing cognito userpool users on AWS lambda

I'm trying to list all of my cognito users in my lambda function, however i get nothing in the return as if the callback not getting executed. What am I doing wrong?
The output of the code below just gives me a hello in the console.
var AWS = require("aws-sdk");
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
export async function main() {
console.log("hello")
var params = {
UserPoolId: "myuserpoolid",
AttributesToGet: ["username"]
};
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
if (err) {
console.log(err, err.stack);
return err;
} else {
console.log(data);
return data;
}
});
}
First of all, the structure of the code is wrong. The header of Lambda function should have a certain structure, either using async function or non-async function. Since you are using non-async code in your example I will show you how to do the later.
var AWS = require("aws-sdk");
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
exports.handler = function(event, context, callback) {
console.log("hello")
var params = {
UserPoolId: "myuserpoolid",
AttributesToGet: ["username"]
};
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
if (err) {
console.log(err, err.stack);
callback(err) // here is the error return
} else {
console.log(data);
callback(null, data) // here is the success return
}
});
}
In this case, Lambda will finish only when callback is called (or when it times out).
Similarly, you can use async function but you will need to restructure your code accordingly. Here is an example taken from official docs. Note how the promise wrapper is used.
const https = require('https')
let url = "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html"
exports.handler = async function(event) {
const promise = new Promise(function(resolve, reject) {
https.get(url, (res) => {
resolve(res.statusCode)
}).on('error', (e) => {
reject(Error(e))
})
})
return promise
}
For AttributesToGet, don't use username because it is one of the fields that always gets returned. The following are members of the Attributes array, and can be used in the AttributesToGet field:
sub, email_verified, phone_number_verified, phone_number, email.
e.g.
AttributesToGet: ["email","email_verified"]

How to use AWS Lambda to run as background task for a limited of time?

What I am trying to do is to trigger lambda to run as a background for a limited amount of time (less than limited lambda timeout). I use setInterval and setTimeout but they don't work properly with async/await. Note that the code works if running locally as a node app. I have my code as below:
const mqtt = require('async-mqtt');
module.exports.startPolling = function(event, context, callback) {
context.callbackWaitsForEmptyEventLoop = false
let runInterval = setInterval( async() => {
await run("tram", "#");
}, 1000);
setTimeout(() => { clearInterval(runInterval)}, 5000);
}
const run = async(mode, route) => {
console.log("Starting");
try {
client = await mqtt.connectAsync('mqtts://mqtt.hsl.fi:8883/');
await client.subscribe(`/hfp/v2/journey/+/vp/${mode}/${route}`);
response = await getMessageAsync(client, "message");
console.log(response.toString());
await client.end();
console.log("Done");
} catch (e){
console.log(e.stack);
process.exit();
}
}
const getMessageAsync = (client, ...args) => {
return new Promise((resolve, reject) => {
client.on(...args, (topic, message) => {
resolve(message);
});
});
}

AWS lambda exits before callback hit

I am experiencing an issue with AWS Lambda with Node 8.10 existing early with the implicit null returned callback:
const request = require('request');
exports.handler = async (event, ctx, callback) => {
console.log(`Fetching '${event.url}'...`);
request(event.url, (err, body) => {
if (err) return callback(err);
return callback(null, body);
});
}
This is a basic example of the problem I have in my own much larger lambda function. Essentially, when I make a call to a 3rd party resource, the Lambda finishes before the callback is reached.
I have read several documentation pages and SO posts that refer to:
callbackWaitsForEmptyEventLoop but setting this to false does not change the behaviour.
I have managed to force it to wait for the response by wrapping the code in a promise then using await before calling callback but is this really necessary?
exports.handler = async (event, ctx, callback) => {
const executeLambda = new Promise((resolve, reject) => {
console.log(`Fetching '${event.url}'...`);
request(event.url, (err, body) => {
if (err) return reject(err);
return resolve(body);
});
});
await executeLambda
.then((res) => callback(null, res))
.catch(callback);
}
Note: I am mocking this locally with docker run --rm -v "$PWD":/var/task lambci/lambda:nodejs8.10 index.handler '{"url": "https://www.google.com"}'
The problem was that I declared the handler with async. The following works:
const request = require('request');
exports.handler = (event, ctx, callback) => {
console.log(`Fetching '${event.url}'...`);
request(event.url, (err, body) => {
if (err) return callback(err);
return callback(null, body);
});
}

Resources