1

I have an async function that needs to return an array of user_ids. My code is like:

async function getUserIds(option) {
  let user_ids = [];

  if (option === 'test') {
    const departments = await Departments.find({}).exec()
      .catch(() => console.log('ERROR');

    console.log('BEFORE LOOP');

    await departments.forEach(async(dpt) => {
      const pop = await Populations.findOne({_id: dpt.id}) 
        .catch(() => console.log('ERROR');

      console.log('work...');

      if (pop && (pop.max > pop.min)) {    
        const _users = await User.find({_id: {$in: pop.ids}}).exec()
          .catch(() => console.log("ERROR");

        user_ids = user_ids.concat(_users.map((u) => u._id));
        console.log('finished work...');
      }
    });

    return user_ids;
}

async function main() {
    let user_ids = await getUserIds('test');
    console.log(user_ids);
}

Now this is always returning an empty array [] and I can see the console logs in an asynchronous way:

BEFORE LOOP
[]   --> the return 
work...
work...
work...
work...
work...
work...
finished work...
finished work...
work...
finished work...
work...
finished work...
finished work...

I guess this line await departments.forEach(async(dpt) => { is not really "awaiting" so what can I do, what I'm doing wrong ?

PepperoniPizza
  • 8,842
  • 9
  • 58
  • 100

2 Answers2

3

The async/await are a sugar way to use Promise.

async is like new Promise() and await is as .then().

Use Promise.all() to wait till all Promise's resolves before continue.

In your case you should use Array.prototype.map() instead of Array.prototype.forEach(). forEach() returns undefined and you are using await. The .map() will return an Array of Promise's as:

// Using .map()
departments = [ Promise, Promise, Promise ] // await till all Promise resolves to go foward.

// Using .forEach()
departments = 'undefined' // nothing to await for.

await Promise.all(
        departments.map(async(dpt) => {
            const pop = await Populations.findOne({_id: dpt.id})
                .catch(() => console.log('ERROR');

            console.log('work...');

            if (pop && (pop.max > pop.min)) {    
                const _users = await User.find({_id: {$in: pop.ids}}).exec()
                    .catch(() => console.log("ERROR");

                user_ids = user_ids.concat(_users.map((u) => u._id));
                
                console.log('finished work...');
            }
       })
);
Iago Calazans
  • 329
  • 1
  • 10
1

You should await for department promises before returning the result from function. The line forEach does nothing in your case, either replace it with map or with for of

with map example

...
const promises = departments.map(async(dpt) => {
  const pop = await Populations.findOne({_id: dpt.id}) 
    .catch(() => console.log('ERROR');

  console.log('work...');

  if (pop && (pop.max > pop.min)) {    
    const _users = await User.find({_id: {$in: pop.ids}}).exec()
      .catch(() => console.log("ERROR");

    user_ids = user_ids.concat(_users.map((u) => u._id));
    console.log('finished work...');
  }
});

await Promise.all(promises)

return user_ids
The Reason
  • 7,705
  • 4
  • 24
  • 42