PTRFRLL
PTRFRLL

Reputation: 27

Node.js Wait for nested promises with Promise.all

I'm trying to wait for all of my database calls in a loop to complete before I proceed. I add each call, which are promises, to an array and then use Promise.all() to wait for all of them. However, there are nested DB calls like user.increment() or user.create() which Promise.all() does not seem to wait for. The output of this snippet usually goes:

User found, incrementing wins...
Promise.all()
User updated

The Promise.all block is being run before the nested DB calls complete. I feel like I'm missing something really easy.

let dbCalls = [];
for(var i = 0; i < users.length; i++){
    let messageUser = users[i];
    //try to find user by id
    dbCalls.push(db.user.findOne({
            where: {
                id: messageUser.id
            }
        }).then(function(user){
            if(user){
                //found user, increment them
                user.increment('wins').then((user) => {
                    console.log('User found, incrementing wins...');
                    user.reload();
                }).then(user => {
                    console.log('User updated')
                    return user;
                });
            } else{
                //user wasn't found, create them
                console.log(`${messageUser.username} was not found, creating user...`);
                db.user.create({
                    username: messageUser.username,
                    id: messageUser.id,
                    wins: 1
                }).then(function(user){
                    console.log('created user');
                    return user;
                });
            }
        }).then(res => {
            return res;
        })
    );
}

Promise.all(dbCalls).then(res =>{
    console.log('Promise.all()' + res);
});

Upvotes: 2

Views: 1414

Answers (3)

CertainPerformance
CertainPerformance

Reputation: 370679

You need to chain all of the internal promises to that the item that gets pushed to the array is the full chain. For better readability, consider extracting the increment and create to their own function:

const increment = user => user.increment('wins').then((user) => {
  console.log('User found, incrementing wins...');
  return user.reload();
}).then(user => {
  console.log('User updated')
  return user;
});
const create = user => {
  //user wasn't found, create them
  console.log(`${messageUser.username} was not found, creating user...`);
  return db.user.create({
    username: messageUser.username,
    id: messageUser.id,
    wins: 1
  }).then(user => {
    console.log('created user');
    return user;
  });
};

const dbCalls = users.map(messageUser => db.user.findOne({
  where: {
    id: messageUser.id
  }
}).then(user => (
  user
  ? increment(user)
  : create(user)
)));

Promise.all(dbCalls).then(res =>{
  console.log('Promise.all()' + res);
});

Upvotes: 0

libik
libik

Reputation: 23029

You forgot to return that promises. So they are not included in your promise chain

Just change lines with db-promises into return user.increment('wins').then((user) => {

Upvotes: 2

Dan
Dan

Reputation: 3815

Since you are running multiple DB queries that require one to finish before the other one, you should look in NODE.js waterfall calls. It allows promises to be used and you can set which ones require other to finish first before firing.

https://www.npmjs.com/package/promise-waterfall

there is one good example

This library goes as far as even allowing you to wait for a returned value to use in another async call.

Upvotes: 0

Related Questions