Promises in Javascript

ยท

6 min read

Hey all! Today we'll learn about promises in JS. I promise I will do my best to explain this concept ๐Ÿ˜œ

Lets consider a real life example. Omkar has borrowed money from Rohan. Now, Rohan wants his money back and so he keeps asking Omkar when he will return back his money. So, to avoid this constant pestering, Omkar promises Rohan that he will call Rohan once he has enough money to pay him back. This way Rohan does not have to stop all his other important tasks just for calling Omkar. And once Omkar has enough money he calls Rohan and pays him back. Everyone happy!

P.S. this is not a real life example. ๐Ÿ˜œ

This is very similar as to how promises in javascript work. When some task requires an uncertain amount of time, the program, instead of waiting for the task's completion gets a promise object in return from that heavy task and carries on with its job. Once the task completes (or fails) it will notify the program about it and the program can decide what it has to with its output.

Creating a promise

A basic promise object syntax would be like:

let promise = new Promise(function (resolve, reject){
   /* some heavy or time taking task */
})

The function passed inside Promise constructor is called as executor and it takes two callback functions as arguments - resolve and reject.

These callbacks are provided by Javascript itself, we just use them once our task completes (resolve) or fails (rejects). Note that the executor starts executing as soon as the Promise object is created.

A promise object consist of two properties:

  • State (initially "pending")
  • Result (initially "undefined")

Promise states

A promise, at any instance, can be in one of these states: pending, fulfilled or rejected.

Initially, when the promise object is just created and executor starts running, the promise will be in "pending" state.

And once the task successfully completes the program will call resolve callback function along with the result. This will put the promise in "fulfilled" state.

In case the the task fails for some reason, it will call the reject callback function along with the error.

Lets test this with below example:

let myPromise = new Promise(function(resolve, reject) {

  setTimeout(() => resolve("done"), 5000);

});

Lets understand the code first. We have created a new Promise object named "myPromise". We passed in a function to it which takes resolve and reject callback functions (which Javascript provides out of the box to us). Inside this function, we have our core time-taking logic. For now we have just used setTimeout which will resolve the promise after 5 seconds. While resolving it will return "done" which would be our actual data we required.

Let's try running this code.

image.png

As we can see, the our promise object is in "pending" state just after its creation. Lets take a break of 5 seconds and take a look at it again.

image.png

Hurray! Our promise got successfully completed! Lets try one example where our promise gets rejected (me remembering my JEE exam)

Rejected gif

image.png

In this example, our executor called reject callback function which resulted in our promise being in "rejected" state

.then() and .catch()

Usually we will want to do some actions after out promise settles (resolves or rejects). Here then and catch comes in play.

Lets see the syntax first:

let myPromise = new Promise(function(resolve, reject) {

});

myPromise.then(functionToCallOnSuccess, functionToCallOnReject)

then takes two callback functions as arguments- first to call on success and second to call if promise gets rejected

onSuccess callback receives the resolved data from the promise

onReject callback receives the error data sent with reject()

then part will only execute once your promise settles

we can also use .catch() to catch only the errors. Its just a .then() with its first argument as null

Lets try this out for more better understanding:

let myPromise = new Promise(function(resolve, reject) {

  setTimeout(() => resolve("gifts"), 5000);

});

myPromise.then((receivedData) => console.log(" I got " , receivedData), (error) => console.log("The task failed with " , error ))

image.png

In above example, we have added a then part to our previous example. We will printing a message along with the data if our promise is fulfilled or we will be printing an error message along with the error passed to us by reject().

If you run this example, you will see that we will only get our output message after 5 seconds because as we learned earlier then part only executes after the promise gets settled.

Notice how only the onSuccess callback function got executed and the onReject callback was skipped.

Lets try one where our promise is rejected:

let myPromise = new Promise(function(resolve, reject) {

  setTimeout(() => reject("404 - no gifts found"), 5000);

});

myPromise.then((receivedData) => console.log(" I got " , receivedData), (error) => console.log("The task failed with " , error ))

image.png

Here, we can see that only our onReject callback was executed.

We can also get the exact outcome using catch.

let myPromise = new Promise(function(resolve, reject) {

  setTimeout(() => reject("404 - no gifts found"), 5000);

});

myPromise.then((receivedData) => console.log(" I got " , receivedData)).catch((error) => console.log("The task failed with " , error ))

image.png

.finally()

Just like in a regular try...catch block, here also the code inside finally block will execute no matter if your promise is resolved or rejected

Its mostly used for cleaning up some data or to stop your loaders.

Look at below example for syntax:

let myPromise = new Promise(function(resolve, reject) {

  setTimeout(() => resolve("gifts"), 5000);

});

myPromise.then((receivedData) => console.log(" I got " , receivedData)).finally(() => console.log("Everyone had dinner!"))

Things to note about finally() are:

  • finally() does not receive any arguments. This makes sense as its not concerned whether or not our promise was resolved or rejected. It's going to get executed anyways! Yup, finally is mean!

  • If, in case, some resolved data or error is passed to finally block, it passes it further along. Ok, maybe not that mean.

See below example for better understanding:

let myPromise = new Promise(function(resolve, reject) {

  setTimeout(() => resolve("gifts"), 5000);

});

myPromise.finally(() => console.log("Everyone had dinner!")).then((receivedData) => console.log(" I got " , receivedData))

Here, finally() comes before then(). When the promise is resolved and the data is passed to finally block, it passes it the next handler. In this case the next then() block for processing.

Output of above example:

image.png

That's it for today! I hope my promise of making you understand promises was resolved ๐Ÿ˜œ.

Will make another blog on some other cool Promise methods like any(), all(), allSettled() and race() which deals with multiples Promises. Till then, happy coding!

ย