[Fixed] Node.js .map function causing express server to return the response object early

Issue

My goal is to create an abstracted POST function for an express running on Node similar to Django’s inbuilt REST methods. As a part of my abstracted POST function I’m checking the database (mongo) for valid foreign keys, duplicates, etc…, which is where we are in the process here. The function below is the one that actually makes the first calls to mongo to check that the incoming foreign keys actually exist in the tables/collections that they should exist in.

In short, the inbuilt response functionality inside the native .map function seems to be causing an early return from the called/subsidiary functions, and yet still continuing on inside the called/subsidiary functions after the early return happens.

Here is the code:

const db = require(`../../models`)

const findInDB_by_id = async params => {

  console.log(`querying db`)

  const records = await Promise.all(Object.keys(params).map(function(table){
    return db[table].find({
      _id: {
        $in: params[table]._ids
      }
    })
  }))
  console.log('done querying the db')

  // do stuff with records
  return records
}

// call await findIndDB_by_id and do other stuff
// eventually return the response object

And here are the server logs

querying db
POST <route> <status code> 49.810 ms – 9 //<- and this is the appropriate response
done querying the db
… other stuff

When I the function is modified so that the map function doesn’t return anything, (a) it doesn’t query the database, and (b) doesn’t return the express response object early. So by modifying this:

const records = await Promise.all(Object.keys(params).map(function(table){
    return db[table].find({ // going to delete the `return` command here
      _id: {
        $in: params[table]._ids
      }
    })
  }))

to this

const records = await Promise.all(Object.keys(params).map(function(table){
    db[table].find({ // not returning this out of map
      _id: {
        $in: params[table]._ids
      }
    })
  }))

the server logs change to:

querying db
done querying the db
… other stuff
POST <route> <status code> 49.810 ms – 9 // <-appropriate reponse

But then I’m not actually building my query, so the query response is empty. I’m experiencing this behavior with the anonymous map function being an arrow function of this format .map(table => (...)) as well.

Any ideas what’s going on, why, or suggestions?

Solution

Your first version of your code is working as expected and the logs are as expected.

All async function return a promise. So findInDB_by_id() will always return a promise. In fact, they return a promise as soon as the first await inside the function is encountered as the calling code continues to run. The calling code itself needs to use await or .then() to get the resolved value from that promise.

Then, some time later, when you do a return someValue in that function, then someValue becomes the resolved value of that promise and that promise will resolve, allowing the calling code to be notified via await or .then() that the final result is now ready.

await only suspends execution of the async function that it is in. It does not cause the caller to be blocked at all. The caller still has to to deal with a promise returned from the async function.


The second version of your code just runs the db queries open loop with no control and no ability to collect results or process errors. This is often referred to as "fire and forget". The rest of your code continues to execute without regard for anything happening in those db queries. It’s highly unlikely that the second version of code is correct. While there are a very few situations where "fire and forget" is appropriate, I will always want to at least log errors in such a situation, but since it appears you want results, this cannot be correct

Leave a Reply

(*) Required, Your email will not be published