Promises in Javascript

Handling asynchronous tasks in web development is very common. One way to handle this is through promises.

Let’s say that we have the following code in that we are running multiple print commands

console.log('step 1');
console.log('step 2');
console.log('step 3');
console.log('step 4');
console.log('step 5');
console.log('step 6');
console.log('step 7');
console.log('step 8');

The way the javascript interpreter works is that it runs the code line by line. And in the above example if any of the steps is taking an abnormal amount of time then javascript will wait for that step to finish before moving on to the next step. Now let’s say step 5 is taking 10 minutes to finish then our application will be stuck for 10 minutes before moving on to the next step.

Now here come the promises. Promises allow you to write asynchronous code. In which the javascript does not wait to complete that operation, rather, simply place it in the queue and cater to it from time to time, until it is completed.

How to create promise?

To create a promise we use the built-in javascript promise constructor. So let’s create a promise.

new Promise((resolve, reject) => {
    // ...
    // ...
    // ...
});

To define a promise we use new Promise and pass it a callback that will be the executor where all our logic will be. And this callback accepts two parameters first is resolve and the second is reject. So you call the resolve method when everything goes well, and the promise was successful, and you want to return the value. Where reject is called when a failure happens or the operation was not successful due to some errors.

So let’s simulate an API call where we will wait for two seconds, and then we will return some dummy users.

const getUsers = new Promise((resolve, reject) => {
    window.setTimeout(() => {
        resolve([
            'john',
            'doe',
        ]);  
    }, 2000);
});

Now when you will run the above code the getUsers will contain the promise. And this promise will have three methods.

  • then
  • catch
  • finally
getUsers
    .then((users) => {
        console.log(users); // ['john', 'doe']
    })
    .catch((error) => {
        console.log(error);   // error...
    })
    .finally(() => {
        console.log('promise settled');    // promise settled
    });

then is called when the promise is successful. So whenever you will call the resolve method or whatever you will pass to it will be passed to then callback. As in the above example, the then method after two seconds received the list of users that was passed to the resolve method.

Similar to then there’s catch method. It will receive errors. When you will create and execute the above promise you will not get any error. So if we update the above example and replace resolve with reject like this.

const getUsers = new Promise((resolve, reject) => {
    window.setTimeout(() => {
        reject('failed to get users');  
    }, 2000);
});

Now on executing the above promise you will see an error after two seconds.

The third method in the promised object is finally. Which is called irrespective of the success or failure of the promise. Now try running the above promises with resolve and reject. You will get promise settled in both cases after the execution of the then or catch.

States of promise

A promise can in one of the four states at any point in time.

Pending State When a promise is yet to be executed.

Resolved State Promise was successful and resolve is called. In this case the then callback is called.

Rejected State Promise action is failed and reject is called. In this case, the catch callback is called.

Settled State Promise was either successful or failed.

Multiple promises

You can also run multiple promises in parallel. There are different ways to achieve that where each method behaves differently.

Promise.all

Wait until all promises are resolved or any of them is rejected.

Promise.all([
  // wait two seconds and resolve
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Promise 1");
    }, 2000);
  }),
  // wait three seconds and resolve
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Promise 2");
    }, 3000);
  }),
])
  .then((data) => {
    console.log(data); // ['Promise 1', 'Promise 2']
  })
  .catch((error) => {
    console.log(error);
  });

Promise.allSettled

Unlike Promise.all it doesn’t fail on the failure of any of the promises and returns all the results with promise status.

Promise.allSettled([
  // wait two seconds and resolve
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Promise 1");
    }, 2000);
  }),
  // wait one second and reject
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("Promise 2");
    }, 1000);
  }),
])
  .then((data) => {
    console.log(data); // [{status: "fulfilled", value: "Promise 1"}, {status: "rejected", reason: "Promise 2"}]
  })
  .catch((error) => {
    console.log(error);
  });

Promise.race

Wait until any of the promises is resolved or rejected. In the example, it will print Promise 2 as it’s the first one to reject.

Promise.race([
  // wait two seconds and resolve
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Promise 1");
    }, 2000);
  }),
  // wait one second and reject
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("Promise 2");
    }, 1000);
  }),
])
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.log(error); // Promise 2
  });

Promise.any

Returns a single promise as soon as any of the promise from the list is fulfilled.

Promise.any([
  // wait two seconds and resolve
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Promise 1");
    }, 2000);
  }),
  // wait one second and resolve
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("Promise 2");
    }, 1000);
  }),
])
  .then((data) => {
    console.log(data); // Promise 1
  })
  .catch((error) => {
    console.log(error);
  });

And that wraps it up for this article. Feel free to leave your feedback or questions in the comments section.