Promise.all vs Promise.allSettled, which is better?

Definitions and Differences

Promise.all(): This Promise method receives an array of promises as input and when all promises are resolved it fulfils the returning promise, however, if there is an error with any of the promise inputs it rejects the returning promise with the error of the first failed promise input.

Let's write some code and see how it works below:

// Happy Case
const promise1 = Promise.resolve('happily completed 1')
const promise2 = Promise.resolve('happily completed 2')

Promise.all([promise1, promise2])
    .then(results => 
    { 
     console.log(results);
     return results;
    });

// Expected Output from on the log
// [ 'happily completed 1', 'happily completed 2' ]

// Failure Case
const promise1 = Promise.resolve('happily completed 1')
const promise2 = Promise.reject(new Error('unhappily completed 2'))

Promise.all([promise1, promise2])
    .then(results => 
    { 
     console.log(results);
     return results;
    });

// Expected Unhandled Error thrown on the log  
//const promise2 = Promise.reject(new Error('unhappily completed 2'))
//                                ^
// Error: unhappily completed 2
//    at Object.<anonymous> (/home/ .js:2:33)
//    at Module._compile (node:internal/modules/cjs/loader:1097:14)
//    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1149:10)
//    at Module.load (node:internal/modules/cjs/loader:975:32)
//    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
//    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
//    at node:internal/main/run_main_module:17:47
// }

From the failure case in the snippet above, using promises without being mindful of unexpected errors is not good practice. A cleaner and safer way to use Promise.all would be as described below, this way the entire code does not blow up when there is an error.

 // Failure Case
const promise1 = Promise.resolve('happily completed 1')
const promise2 = Promise.reject(new Error('unhappily completed 2'))

Promise.all([promise1, promise2])
    .then(results => 
    { 
     console.log(results);
     return results;
    }).catch((error) => { console.log(error) } )

Promise.allSettled(): This Promise method receives an array of promises as input and when all promises are resolved it fulfils the returning promises as an array of objects with keys status and value for the promise inputs (pretty cool right?). This time if there is an error with any of the promise inputs it does not reject the returning promise with an error, it just indicates this appropriately in the status of the corresponding promise in the promise output.

Let's see how it works here:

// Happy case

const promise1 = Promise.resolve('happily completed 1')
const promise2 = Promise.resolve('unhappily completed 2')

Promise.allSettled([promise1, promise2])
    .then(results => 
    { 
     console.log(results);
     return results;
    }).catch((error) => { console.log(error) } )

// Expected Output from on the log
// [
//   { status: 'fulfilled', value: 'happily completed 1' },
//   { status: 'fulfilled', value: 'unhappily completed 2' }
// ]

// Failure Case
const promise1 = Promise.resolve('happily completed 1')
const promise2 = Promise.reject(new Error('unhappily completed 2'))

Promise.allSettled([promise1, promise2])
    .then(results => 
    { 
     console.log(results);
     return results;
    }).catch((error) => { console.log(error) } )

// Expected Output from on the log
// [
//   { status: 'fulfilled', value: 'happily completed 1' },
//   {
//     status: 'rejected',
//     reason: Error: unhappily completed 2
//         at Object.<anonymous> (/runtime/javascript/3zmjhp699_3zmnyema7/HelloWorld.js:2:33)
//         at Module._compile (node:internal/modules/cjs/loader:1103:14)
//         at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10)
//         at Module.load (node:internal/modules/cjs/loader:981:32)
//         at Function.Module._load (node:internal/modules/cjs/loader:822:12)
//         at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
//         at node:internal/main/run_main_module:17:47
//   }
// ]

From the definitions and code snippets above, we have seen the output of both Promise methods and now have an idea of how they work, the big question is which is better?


Answer to the Big Question

Both methods are effective depending on the use case:

Promise.all: This is effective when working with promises that are highly dependent on each other.

Promise.allSettled: On the other hand, this is effective when working with independent promises.

However, see a few advantages of these methods:

  • Helps write cleaner code when working with multiple promises by eliminating the need for chaining promises, which can be pretty messy.

  • A cleaner way of handling unexpected errors when working with multiple expensive promises like (Database queries, Network calls e.t.c).

Thanks and Happy <Coding!>

#BackendStrories