Promise in JavaScript simplified!

Promise in JavaScript simplified!

What's promise in JS?

Promise in JavaScript is just like a promise in real life. It enables us to perform asynchronous tasks in JavaScript and always returns a result whether the promise is fulfilled or not.

Let’s have a look at below syntax for creating a promise:

const myPromise = new Promise((resolve, reject)) {
    resolve('my promise has been resolved');
    reject('my promise has been rejected');
}

As we can see in the above example, a promise can be created using the constructor function along with new keyword.

The constructor function takes two callback functions as arguments; resolve() and reject().

We can call the resolve() or the reject() function based on certain conditions to return the result.

Let’s write a function that either resolves or rejects and returns the result based on certain conditions.

const isAdult = (name, age) => {
    return new Promise((resolve, reject) => {
       if (age < 18) {
           reject(`${name} is not an adult`);
       }
       else {
           resolve(`${name} is an adult`);
       }
    });
}

isAdult("Shahbaz",25); // resolved
isAdult("Umair", 16); // rejected

Promise States

A promise can be in 3 states:

  1. Pending
  2. Fulfilled
  3. Rejected

When the promise is called, it gets into the pending state and changes its state after getting resolved or rejected.

Let’s add a setTimeout to make our promise wait before getting resolved or rejected.

const isAdult = (name, age) => {
    return new Promise((resolve, reject) => {
       setTimeout(() => {
           if (age < 18) {
                reject(`${name} is not an adult`);
            }
            else {
               resolve(`${name} is an adult`);
            }
       }, 3000)
    })     
}

isAdult("Shahbaz",25); // resolved
isAdult("Umair", 16); // rejected

Now the promise will have to wait for 3 seconds before getting resolved or rejected.

So, the promise will remain in a pending state until it gets resolved or rejected.

Now that we are aware of how the promise is created, let’s have a look at how to call the promise and perform certain actions based on the data received.

isAdult("Shahbaz", 25)
    .then(data => console.log(data))
    .catch(error => console.log(error));

We can use .then() to perform certain action when the promise is fulfilled and .catch() to handle an error when the promise is rejected.

Both the blocks take a callback function as an argument, the data returned by the promise is passed in the callback function of .then() block in case of success and the error returned by the promise is passed in the callback function of .catch() block.

Promise Chaining

You can chain the promises in a number of .then() blocks as whatever you return from .then() also becomes a promise.

isAdult("Shahbaz", 25)
    .then(data => `${data} and is eligible for voting`)
    .then(data => console.log(data))
    .catch(error => console.log(error));

You can chain as many .then() as you want and each .then() block will receive the data from its previous block.

You can also add the .finally() block if you want to perform certain actions finally after the promise is resolved or rejected.

isAdult("Shahbaz", 25)
    .then(data => `${data} and is eligible for voting`)
    .then(data => console.log(data))
    .catch(error => console.log(error))
    .finally(() => console.log("Action completed"))

Handling multiple promises

Promise.all()

If we have to make multiple promise calls at a time, we can use Promise.all() to handle multiple promises at once.

It accepts an array of promises and executes the .then() block once all the promises are resolved, by passing an array of results in the callback function of .then() block however it executes the .catch() block if any of the promise is rejected.

const promise1 = new Promise((resolve, reject)=> resolve("Promise 1 resolved"));
const promise2 = new Promise((resolve, reject)=> resolve("Promise 2 resolved"));
const promise3 = new Promise((resolve, reject)=> resolve("Promise 3 resolved"));

Promise.all([promise1, promise2, promise3])
    .then(messages => console.log(messages))
    .catch(error => console.log(error));

// ["Promise 1 is resolved", "Promise 2 is resolved", "Promise 3 is resolved"]

In the above example all the promises gets resolved, so we get an array of messages in the .then() block.

Now let’s see what happens if a promise gets rejected.

const promise1 = new Promise((resolve, reject)=> resolve("Promise 1 resolved"));
const promise2 = new Promise((resolve, reject)=> reject("Promise 2 rejected"));
const promise3 = new Promise((resolve, reject)=> reject("Promise 3 rejected"));

Promise.all([promise1, promise2, promise3])
    .then(messages => console.log(messages))
    .catch(error => console.log(error));

// Promise 2 rejected

In the above example, even though the first promise is getting resolved, it’s getting into the .catch() block since the second promise is getting rejected.

Notice that the third promise will also be rejected however the result is returned as soon as the one rejected promise is found.

Promise.race()

It is similar to Promise.all() except the fact that it does not return all the resolved promises, rather it only returns one promise which is resolved first.

const promise1 = new Promise((resolve, reject)=> resolve("Promise 1 resolved"));
const promise2 = new Promise((resolve, reject)=> resolve("Promise 2 resolved"));
const promise3 = new Promise((resolve, reject)=> resolve("Promise 3 resolved"));

Promise.race([promise1, promise2, promise3])
    .then(messages => console.log(messages))
    .catch(error => console.log(error));

// Promise 1 resolved

In the above example, it only returns the result of promise one since it got resolved first and ignored all the other promises.

In case if a promise is rejected before the resolution of any other promise, it will return the rejected state in the .catch() block and ignore all the promises.

const promise1 = new Promise((resolve, reject)=> reject("Promise 1 rejected"));
const promise2 = new Promise((resolve, reject)=> resolve("Promise 2 resolved"));
const promise3 = new Promise((resolve, reject)=> resolve("Promise 3 resolved"));

Promise.race([promise1, promise2, promise3])
    .then(messages => console.log(messages))
    .catch(error => console.log(error));

// Promise 1 rejected

Promise.any()

Promise.any() ignores the rejected promises and returns the first promise that gets resolved.

Even if a promise is rejected before the resolution of any other promise, it will ignore the rejected promise and wait for the resolved promise. As soon as it gets the first resolved promise, it returns the result in the .then() block.

const promise1 = new Promise((resolve, reject)=> reject("Promise 1 rejected")); const promise2 = new Promise((resolve, reject)=> resolve("Promise 2 resolved")); const promise3 = new Promise((resolve, reject)=> resolve("Promise 3 resolved"));

Promise.any([promise1, promise2, promise3])
    .then(messages => console.log(messages))
    .catch(error => console.log(error));

// Promise 2 resolved

As we can see in the above example, it returns the result of promise 2 since the first one gets rejected.

However, it returns an AggregateError if none of the promises get resolved.

const promise1 = new Promise((resolve, reject)=> reject("Promise 1 rejected"));
const promise2 = new Promise((resolve, reject)=> reject("Promise 2 rejected"));
const promise3 = new Promise((resolve, reject)=> reject("Promise 3 rejected"));

Promise.any([promise1, promise2, promise3])
    .then(messages => console.log(messages))
    .catch(error => console.log(error));

// AggregateError: All promises were rejected

That's all for this blog!


I hope you found it helpful. Feel free to add your thoughts and feedback in the comment section.

Have a great day!

Did you find this article valuable?

Support Shahbaz Khan's Blog by becoming a sponsor. Any amount is appreciated!