Sal
Sal

Reputation: 1691

Async/await not working AWS lambda, skipping everything after await

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

Upvotes: 8

Views: 6735

Answers (3)

React Dev
React Dev

Reputation: 420

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

Upvotes: 0

Sal
Sal

Reputation: 1691

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.

Upvotes: 1

saart
saart

Reputation: 402

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

Upvotes: 0

Related Questions