How Promises work in Javascript

Javascript is Asynchronous, i.e; there will be some code blocks in your program that will wait for other functions/code to execute. Most times this "some code" will be a Callback function that waits for certain processes to finish and then execute.

eg.- setTimeout(), setInterval() methods, an external API call that fetches the data and takes some time.

And in real scenarios, there are going to be multiple of these callback functions in your code waiting to be called.

If only there was some way to handle these callbacks and get....... Oh! wait there is. In fact, there are two.

  • Promises

  • Async/Await

Let's see what they do one at a time.

Promise

In the movie Avengers Endgame, when Steve Rogers(Captain America) made the promise to Tony Stark to bring everyone back to life with the help of the Time Machine Tony helps create, it could result in three scenarios -

  1. They try getting the stones (which is half of the movie) ;

    --> Pending

  2. They get the Infinity Stones and succeed (which fortunately they did)

    --> Completion

  3. Or they don't get the infinity Stones and fail.

    --> Failure

A Promise is just that. An object which tracks and represents the eventual state(pending OR completion OR failure) of the callback function.

This Asynchronous method instead of returning a value immediately returns a promise to supply the value at some point in the future.

Same as the state of our AVENGERS, the promise will be in one of the states:-

  • pending: initial state, neither fulfilled nor rejected.

  • fulfilled: meaning that the operation was completed successfully.

  • rejected: meaning that the operation failed.

const getStones = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(console.log("We've got the Stones Thanos!!"));
  }, 3000);
});

The parameters resolve, reject are the operations/tasks done if the promise is fulfilled or rejected.

In the above code, the setTimeout executes after 3 seconds and the resolve method logs the "We've got the Stones Thanos!!" on the console.

A Promise is 'settled' if it is either fulfilled or rejected.


Okay, till now we saw how one promise is handled. But there are multiple of these callbacks we need to handle, and there might be a scenario where promise A depends on a value that promise B is providing.

For Our Avengers, defeating Thanos in the final battle depended on the stones to get back the army they needed,

So the winBattle promise will depend on the getStones promise to complete.

const winBattle = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(console.log("Avengers Assemble"));
  }, 3000);
});

In these scenarios, a concept known as Promise Chaining is often used

Promise Chaining

winBattle
  .then(getStones)
  .then(someOtherPromise)

Above, winBattle will take the response coming from getStones promise using .then() chaining operator/method when getStones is completed and resolved.

But if getStones gets rejected and does not respond with a value. then we have another chaining operator/method called .catch().

.catch() executes the code when a promise is rejected, i.e; the Avengers didn't get the Stones.

winBattle
  .then(getStones)
  .catch(console.log("Thanos has the last laugh")

This Way we can handle multiple promises coming from multiple callback functions in our Code.

The above code can be written as follows:-

winBattle
  .then(new Promise((resolve, reject) => {
      setTimeout(() => {
       resolve(console.log("We've got the Stones Thanos!!"));
     }, 3000);
   });)
  .catch(console.log("Thanos has the last laugh")

Promise chaining can be a bit tricky when it comes to debugging, as you can already see the code is rather complex in readability.

Well, it works, but there is one better alternative made available to us. Which is

Async/Await

You can learn about them further as they are more often used in Modern Javascript. They have more readability and are better for debugging purposes also.