Promise.all() is not resolving promises in the expected order

17,113

Solution 1

Promise.all does not impose any order on the promises, they fulfill when they fulfill

the third promise "rejects" well after the other two "resolve", so the fact that the first two are resolved before the last is rejected is to be expected

by the way, your first two promises are resolving the value undefined and outputting to the console - which is why you may think that Promise.all is doing something it ought not be doing

Do I need to compose my promises differently to see the behavior I'm expecting?

You wont get exactly the behaviour you are expecting, because you expect the promises to resolve in a particular order, the only way that can happen is for the promise to wait for the previous one to be fulfilled before it can fulfill, so the time would be cumulative, not in parallel, so in that case you wont get the rejection for 7.5 seconds rather than the 5 seconds you "expect"

Solution 2

The then or the catch function of Promise.all is called only once all of the promises have resolved or one of the promises has been rejected. The individual promises can resolve in any order (that's the entire point of promises - they are asynchronous and can complete what they are supposed to whenever they like)

That said, it would be clearer if you tagged your Promise.all console.log differently, like so

Promise.all([secondAsyncFunction(), firstAsyncFunction(), thirdAsyncFunction()])
    .then(function (values) {
        values.forEach(function (value) {
            console.log('then:' + value);
        });

    }).catch(function (err) {
        console.log(err);
    });

You could be mixing up the console.logs in the promise and the one in the then callback.

Solution 3

Just to add to what others have said.

Promise.all does not run sequentially.

Here is an example of running async functions sequentially in ES6

/**
 * Runs async functions sequentially
 * @param Function[]
 * @return Promise<any>
 */
function runSequentially(functions) {
    return functions.reduce((promise, next) => {
        return promise.then(next);
    }, Promise.resolve());
}


/**
 * Full Example
 */

function foo()
{
    return new Promise(( resolve, reject )=>{
        resolve();
    })
}

function boo() {
    return new Promise((resolve, reject) => {
        resolve();
    })
}

function baz() {
    return new Promise((resolve, reject) => {
        resolve();
    })
}

const functions = [foo, boo, baz];

runSequentially(functions).then((result) => {

}).catch((error) => {

});
Share:
17,113
James Hines
Author by

James Hines

Updated on June 04, 2022

Comments

  • James Hines
    James Hines almost 2 years

    If I understand Promise.all() correctly, I would expect this code to take 5 seconds before outputting only the reason for the rejected promise to the console.

    function firstAsyncFunction() {
      return new Promise(function(resolve, reject){
        setTimeout(function(){
          resolve(console.log('First async function has been resolved!'));
        }, 500);
      });    
    }
    
    function secondAsyncFunction() {
      return new Promise(function(resolve, reject) {
        setTimeout(function(){
          resolve(console.log('Second async function has been resolved!'));
        }, 2000);
      });
    }
    
    function thirdAsyncFunction() {
      return new Promise(function(resolve, reject) {      
        setTimeout(function() {              
          reject('Internal server error'); // rejected for some reason
        }, 5000);    
      });
    };
    
    Promise.all([secondAsyncFunction(), firstAsyncFunction(), thirdAsyncFunction()])
        .then(function(values){        
          values.forEach(function(value){
            console.log(value);
          });
    
        }).catch(function(err){
          console.log(err);
        });
    

    Instead, the first two promises resolve, then the final promise rejects. Additionally, the first two promises aren't even resolving in the order in which they are passed in to Promise.all(). Do I need to compose my promises differently to see the behavior I'm expecting?

    Edited

    Promise.all() does indeed wait until all the promises in the iterable passed into it resolve. My first clue was the fact that the console was outputting undefined (thanks Jaromanda X). If I remove the calls to console.log() in the resolve to firstAsyncFunction() and secondAsyncFunction() the following piece of code works exactly as I would expect:

    function firstAsyncFunction() {
      return new Promise(function(resolve, reject){
        setTimeout(function(){
          resolve('First async function has been resolved!');
        }, 1000);
      });    
    }
    
    function secondAsyncFunction() {
      return new Promise(function(resolve, reject) {
        resolve('Second async function has been resolved!');      
      });
    }
    
    function thirdAsyncFunction() {
      return new Promise(function(resolve, reject) {      
        setTimeout(function() {      
          reject('Internal server error');
        }, 5000);
      });
    };
    
    Promise.all([
      thirdAsyncFunction(), 
      firstAsyncFunction(), 
      secondAsyncFunction()
    ])
     .then(function(values){        
       values.forEach(function(value){
         console.log(value);
       });
     })
     .catch(function(err){
       console.log(err);
     });
    

    After five seconds I see "Internal server error" only. Promise.all() rejects the other promises even though they resolve sooner than the promise that was rejected. And yes, the values resolved by Promise.all() will be in the same order as the promises in the iterable passed in as a parameter. Thanks for all your help!

    One more example:

    function firstAsyncFunction() {
      return new Promise(function(resolve, reject){
        setTimeout(function(){
          resolve('First async function has been resolved!');
        }, 1000);
      });    
    }
    
    function secondAsyncFunction() {
      return new Promise(function(resolve, reject) {
        resolve('Second async function has been resolved!');      
      });
    }
    
    function thirdAsyncFunction() {
      return new Promise(function(resolve, reject) {      
        setTimeout(function() {
          resolve({
            users: [
              { name: 'Ronnie', age: 22 },
              { name: 'Bobby', age: 21 },
              { name: 'Ricky', age: 21 },
              { name: 'Mike', age: 20 }
            ]
          });
        }, 5000);
      });
    };
    
    Promise.all([thirdAsyncFunction(), firstAsyncFunction(), secondAsyncFunction()])
      .then(function(values){        
        values.forEach(function(value){
          console.log(value);
        });
      })
      .catch(function(err){
        console.log(err);
      });
    

    After five seconds, this code will output:

    { users: 
       [ { name: 'Ronnie', age: 22 },
         { name: 'Bobby', age: 21 },
         { name: 'Ricky', age: 21 },
         { name: 'Mike', age: 20 } ] }
    First async function has been resolved!
    Second async function has been resolved!
    

    Exactly what we want.

  • Sean Dunford
    Sean Dunford over 7 years
    Doh, I feel dumb. Your example reminded me that I needed to execute the functions in the array. This will probably trip up a lot of people coming from async.parallel because you just store the function and it executes for you.
  • Bodman
    Bodman about 6 years
    @Bergi , Works on my end. Iv updated the example to show the full example. jsfiddle.net/Bodman/8hp1p8cm
  • Bergi
    Bergi about 6 years
    [foo, boo, baz] are no promises. They are functions that return promises. Your code that explicitly annotates promises is an array of promises would not typecheck.
  • Bodman
    Bodman about 6 years
    Yeah, good catch. Looks like it breaks on rejection. I'll fix it.
  • Bodman
    Bodman about 6 years
    @Bergi should be fixed now. Thanks for the input !
  • Bergi
    Bergi about 6 years
    No. Now it's not sequential any more - you really need to use Promise.all if you have an array of promises. The array of functions was fine - it's necessary for doing things sequentially - you just would need to have called it that