joshk132
joshk132

Reputation: 1093

Async function not running some code even inside await

I have a Node.js AWS Lambda function created via the serverless framework. I have multiple helper functions inside it. I am having an issue with one of them due to being async. The function runs and logs out all parts I put comments next to however it doesn't update callDuration. I think that the code is having an issue due to async where it finishes in the wrong order. My goal is to be able to return the callDuration to my main function for further processing. How can I get all code to process/run and be able to meet my goal and have the code run in the right order

Here is the function:

const callAggregate = async (billingData, billingDB) => {
    const accountSid = process.env.TWILIO_ACCOUNT_SID
    const authToken = process.env.TWILIO_AUTH_TOKEN
    const client = require('twilio')(accountSid, authToken)
    // Setup model
    const Billing = billingDB.model('Billing')
    await Billing.findOne({_id: billingData._id}).exec().then(bill => {
      const callArray = bill.callSid
      console.log(bill) // This logs out
      let callDuration = 0
      for (const call of callArray) {
        console.log(call) // This logs out
        client.calls(call)
        .fetch()
        .then(callDetails => {
          console.log(callDetails) // This logs out
          callDuration += callDetails.duration
        })
      }
      console.log(`Billing for ${callDuration} minutes of voice calling for ${billingData._id}`) // This logs out
      Billing.findOneAndUpdate(
        {_id: billingData._id},
        { $inc: { call_duration: callDuration }, callSid: []},
        (err, doc) => {
            if(err) {
                console.log(err)
            }
        }
      )
      return callDuration
    })
}

Upvotes: 1

Views: 1159

Answers (1)

jfriend00
jfriend00

Reputation: 707238

This is a case of mixing and matching promises with plain callbacks and mixing await with .then(), both of which make proper flow-control and error handling management difficult.

Inside your function which is async and uses await in some places, you also have a promise you are not awaiting (which means it runs open loop and nothing waits for it) and you have a database function that is using a plain callback, not the promise interface so nothing waits for it either.

More specifically, nothing is waiting for this:

client.calls(call).fetch()

So, because of not waiting for the .fetch() to finish, you were attempting to use the variable callDuration before the code was done modifying that variable (giving you the wrong value for it).

Similarly, nothing is waiting for Billing.findOneAndUpdate(...) to complete either.

A clean solution is to switch everything over to promises and await. This involves, using only promises with your database (no plain callbacks) and converting the .then() handlers into await.

async function callAggregate(billingData, billingDB) {
    const accountSid = process.env.TWILIO_ACCOUNT_SID
    const authToken = process.env.TWILIO_AUTH_TOKEN
    const client = require('twilio')(accountSid, authToken)
    // Setup model
    const Billing = billingDB.model('Billing')
    let bill = await Billing.findOne({ _id: billingData._id }).exec();
    const callArray = bill.callSid
    console.log(bill) // This logs out
    let callDuration = 0
    for (const call of callArray) {
        console.log(call) // This logs out
        let callDetails = await client.calls(call).fetch();
        console.log(callDetails) // This logs out
        callDuration += callDetails.duration
    }
    console.log(`Billing for ${callDuration} minutes of voice calling for ${billingData._id}`) // This logs out
    let doc = await Billing.findOneAndUpdate({ _id: billingData._id }, { $inc: { call_duration: callDuration }, callSid: [] }).exec();
    return callDuration
}

Upvotes: 1

Related Questions