8

I'm trying to use AWS lambda to test a few API calls using axios, however I'm having some trouble. Every post I came across said the best way to handle promises in Lambda was to use async/await rather than .then, so I made the switch. When I run the program using node it works perfectly, but when I invoke the Lambda locally, it seems like everything after the axios call is being skipped. When I invoke the Lambda locally without await, the calls after it run fine, but then I'm forced to use .then which the Lambda doesn't wait for anyway. I've increased the Lambda timeout to 900, and I've run sam build before sam invoke local every time.

function checkServers() { console.log("Inside checkServer"); console.log("Before apis to test"); // apisToTest has length of 2 apisToTest.forEach(async (apiToTest) => { console.log("Api to test"); let res = await axios(apiToTest) console.log("x"); // This gets skipped console.log(res); // This gets skipped }) console.log("After api to test") } exports.lambdaHandler = async (event, context) => { console.log("Inside lambda handler"); checkServers(); console.log("After lambda handler"); }; // Used to test app using node command checkServers() 

This yields the following output:

INFO Inside lambda handler INFO Inside checkServer INFO Before apis to test INFO Api to test INFO Api to test INFO After api to test INFO After lambda handler 
9
  • stackoverflow.com/a/37576787/4388775 use rather a for ... of loop instead. Commented Nov 1, 2020 at 8:44
  • Right, forEach doesn’t await async functions, and nothing else in your code is either. Commented Nov 1, 2020 at 8:53
  • Thanks for the reply, I gave this a shot, I changed the forEach to a for ... of and made checkServer async, but it didn't resolve the issue unfortunately, the values after the await line still seem to get skipped. Even if forEach doesn't await async functions, that shouldn't prevent "x" or res (even if it's undefined) from printing, right? Commented Nov 1, 2020 at 8:54
  • 1
    How about try-catching to see if exceptions are just not thrown there? Commented Nov 1, 2020 at 9:42
  • let res = await axios(apiToTest) probably throws an exception. As you are not catching it, code execution is aborted . Commented Nov 1, 2020 at 10:45

3 Answers 3

1

Thanks for all of your replies, unfortunately those weren't the ideal solutions for my use case, though they were very helpful in me coming up with the solution.

async function checkServers() { let emailBody = ""; let callResult = ""; let completedCalls = 0; let promises = []; for (const apiToTest of apisToTest) { await axios(apiToTest).then((res) => { // Do something }).catch((r) => { // Handle error }) } } exports.lambdaHandler = async (event, context) => { context.callbackWaitsForEmptyEventLoop = true; await checkServers(); }; 

To summarize, I replaced the forEach call to a for...of call, changed the checkServers to async, and combined await with .then() and .catch to handle the Promise result. I was unaware that both can be used to do so in tandem. Hope this helps anyone that had an issue similar to the one I had.

Sign up to request clarification or add additional context in comments.

1 Comment

One small suggestion, its a common problem but there looks to be a mixture of the async/await style and the .then() style. Consider: for (const apiToTest of apisToTest) { axios(apiToTest).then((res) => { // Do something }).catch((r) => { // Handle error }) } or try { for (const apiToTest of apisToTest) { const result = await axios(apiToTest); } } catch(err) { // Handle error }
0

you got two options:

  1. set callbackWaitsForEmptyEventLoop to true - and then AWS will wait until the eventloop is empty
  2. await your promises. Note that you didn't await your promises on checkServers. Consider using await Promise.all([...]) to wait for all your promises to finish.

Note: If you will run the lambda again, the events from the previous invocations may "leak" to the next invocation. You can learn about it here: https://lumigo.io/blog/node-js-lambda-execution-leaks-a-practical-guide/

Disclosure: I work in the company that published this blog post

2 Comments

Thanks for the response. I had attempted using await Promise.all(), however the challenge I ran into was that if one API call failed, then the entire result would throw an error. I want to handle individual errors, as I am expecting those to come up, and I wasn't able to find a way to do that with Promise.all()
There are many very elegant ways to handle separate errors. Check out this thread: stackoverflow.com/questions/30362733/…
0

Similar to what saart is saying you could try the below code and see if it works:

async function checkServers() { const promises = apisToTest.map((apiToTest) => axios(apiToTest)) const resolvedPromises = await Promise.all(promises); return resolvedPromises; } exports.lambdaHandler = async (event, context) => { try { const result = await checkServers(); console.log(result); } catch (error) { console.log(error) } }; // Used to test app using node command checkServers(); 

2 Comments

Thanks for your reply. The challenge with Promise.all() was that it would error if one of the Promises errored. I'm expecting API calls to fail from time to time and don't want the entire Promise to fail, rather keep track of what passed and what failed and keep track of those.
Hi @Sal, no problem :) I have updated the code to not make the lambda handler fail. I did this by wrapping it in a try ... catch statement, so when an error is thrown it will just be logged without failing. You can learn more about try catch statements and how they work with promises here: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.