Promise methods - what are they & how to remember them?

Introduction

In this blog, I want to walk you all through promise methods and how to remember what each one does.

Before we begin, one easy way that I follow, to remember stuff for a longer time, or I would rather say to not forget the concepts, I try to relate the meaning of the concept in simple English to the purpose of the concept. If not always, most of the times they seem to go together. (In short - naam mei kya rakha hai? Naam mei hi concept rakha hai ;) which translates to- What's in a name ? The name itself means the concept :P )

One more pointer before we start, remember promise is an object consisting of result and its status, the return values of the input promises in the iterable are one of either a promise object or a promise result. Remember this, it is going to help you logically understand the reason behind the different return values of inputs in these methods.

Okay enough talking already, lets get to the core and understand the methods.

Promise methods

The Promise class has 6 static methods - resolve, reject, all, allSettled, any, race.

Promise.all

let promise = Promise.all(iterable of promises);

As we can see, all method takes an iterable and returns a promise. The promise returned resolves when all the listed promises are fulfilled. The result is the array of all the results of the promises.

Tip: How to remember what is returned here? So we know that a promise object has result and status, and the purpose of Promise.all is to return a promise when all are resolved. This means that we expect all of them to be in fulfilled status. So there is no point in returning the status when we already know that all are expected to fulfill. So the return value is a promise whose result is the array of results of each promise in the same order as the input.

let promise = Promise.all([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(5), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
])
 promise.then(result=>result.forEach(prom=>console.log(prom)))
 // output : 
 //1
 //3
 //5

Now you might wonder, we cannot guarantee that all promises achieve fulfilled status. There might arise a scenario where one or more might get rejected. Yes, you are right! This might happen. And in this case, Promise.all immediately rejects, forgetting about the other ones in the list. Their results are ignored. They might settle but are ignored. And to capture this error, we can provide a catch and handle the error. So when any promise rejects, the error is thrown.

 Promise.all([
   new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
   new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
   new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
 ]).catch(err=>console.log(err)); 
 // output : "Error: Whoops!"

NOTE: If any of the objects in the input is not a promise, it is passed to the resulting array “as is”.

 Promise.all([
   new Promise((resolve, reject) => {
     setTimeout(() => resolve(10), 1000)
   }),
   20,
   30
 ]).then(alert); 
 //output : 10, 20,30

Promise.allSettled

let promise = Promise.allSettled(iterable of promises);

The allSettled method takes an iterable and returns a promise. The promise returned resolves when all the listed promises are settled, which means they can be in a status of either fulfilled or rejected. The result is the array of all the results of the promises.

Tip: How to remember what is returned here? Remember the promise object again? Now, the purpose of Promise.allSettled is to return a promise when all are settled. This means, we expect all of them to be in either fulfilled/rejected status. So while consuming them we might want the result and chaining a promise using .then and .catch gives us an idea whether it was fulfilled or rejected. Logically, the return value is a promise whose result is the array of promise object of each promise in the same order as the input.

let promise = Promise.allSettled([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(5), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
])
promise.then(result=>result.forEach(prom=>console.log(prom))) 
//output:
// {status: 'fulfilled', value: 1}
// {status: 'rejected', reason: 5}
// {status: 'fulfilled', value: 3}

So for each promise we get its status and value/error.

Promise.race

let promise = Promise.race(iterable of promises);

The race method takes an iterable and returns a promise. As the name suggests it has something to do with being fast. So this method waits for the first settled promise and gets its result. Mind here that, a promise that settles first is returned, meaning it could be either of fulfilled or rejected status.

Tip: How to remember what is returned here? As we are expecting a single promise to fulfill, that promise itself is returned.

let promise = Promise.race([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 1000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
])
promise.then(result=>console.log(result)).catch(d=>console.log(d))
//output: Error: Whoops!

Promise.any

let promise = Promise.any(iterable of promises);

The any method takes an iterable and returns a promise. There is a very minor difference between, race and any. Any, as the name suggests, waits only for the first fulfilled promise and gets its result. So this method waits until a promise fulfills and returns its result. After the first fulfilled promise "wins the race", all further results are ignored.

Tip: How to remember what is returned here? As we are expecting a single promise to fulfill, that promise itself is returned.

let promise = Promise.any([
  new Promise((resolve, reject) => setTimeout(() => resolve(new Error("Whoops!")), 1000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
])
promise.then(result=>console.log(result)).catch(d=>console.log(d))
//output: 1

Remember that it is also possible when all promises in the iterable may reject. In this scenarios, an aggregate error os is returned saying all promises are rejected.

let promise = Promise.any([
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(1), 2000)),
  new Promise((resolve, reject) => setTimeout(() => reject(3), 3000))
])
promise.then(result=>console.log(result)).catch(d=>console.log(d))
//output : AggregateError: All promises were rejected

The following 2 methods are rarely used but its always good to know. It helps in creating promises that quickly resolve or reject.

Promise.resolve

let promise = Promise.resolve(value);

Promise.resolve(value) creates a resolved promise with the result value.

let promise = Promise.resolve(5);
promise.then(result=>console.log(result)).catch(d=>console.log(d))
//output: 5

Promise.reject

let promise = Promise.reject(error);

Promise.resolve(value) creates a rejected promise with the given error.

let promise = Promise.reject(new Error);
promise.then(result=>console.log(result)).catch(d=>console.log(d))
//output : Error

So that's it about the promise methods, I hope this blog helps in better understanding the promise methods. Thanks for reading! :)