DEV Community

Cover image for Let's Understand Promises in JavaScript
Vivek Kumar Gupta
Vivek Kumar Gupta

Posted on

Let's Understand Promises in JavaScript

What are Promises?

  • Promise in Javascript is same as we commit a promise in real life. As we all know that promise is made for future, so definately, the outcome will also come in future, which makes the present state of promise is pending.

  • Their are 2 Possible outcomes of a Promise, either the promise will maintained or else it will be broken.

Similarly, In JavaScript, their are 2 possible outcomes of a Promise: Resolve or Reject.


Let's understand this statement with an example.

Suppose, You (Ram) and your friend (Sham) just passed Higher Secondary School and because you both are best friends, so you both decided take admission in the same college to do your bachelor's.

Now, there can be 2 possibility that:

  • Ram and Sham took admission in same college, which means promise is fulfilled or in other words Promise is Resolved.
  • Because of some reason Sham decided join his father's company directly after high school, which means promise which they made is broken or in other words Promise is Rejected.

Different States of Promise in JavaScript

When a Promised is created in JavaScript, it is first initialised with a state of pending.

  1. Pending (Initial State, before the Promise succeeds or fails)
  2. Resolved (Completed Promise)
  3. Rejected (Failed Promise)

Descriptive picture of promise states


What is the need of Promises?

So, Vivek tell me one thing why do we need promises in javaScript instead why can't we use callbacks to make async calls?

Okay, So the answer of your question is:

  • We have callback functions in JavaScript. But, a callback is not a special thing in JavaScript. It is a regular function that produces results after an asynchronous call completes (with success/error).
  • While callbacks are helpful, there is a huge downside to them as well. At times, we may have one callback inside another callback that's in yet another callback and so on, now you're trapped in a callback hell. I'm serious! Let's understand this "callback hell" with an example.
function displayMessage(fn){
   fn("inside displayMessage");

   setTimeout(() => {
     fn("1st work done!");

     setTimeout(() => {
        fn("2nd work done!");

        setTimeout(() => {
           fn("3rd work done!")
        },3000);

     }, 2000);

   },1000);

   fn("all works are done!!");
}

function onDone(statement){
  console.log(statement);
}

displayMessage(onDone);
Enter fullscreen mode Exit fullscreen mode

So, Now lets understand the code line by line:

In this example a function displayMessage is called which recieves a callback function, this displayMessage functions does a simple work that it logs that status of work completions. As we can see that there is a nesting of setTimeout inside every setTimeout(), this nesting can go on, this nesting of function inside another function is called callback hell.

  • Writing this type of code can create many production grade implication like:
    • it makes the code performance slow,
    • it impacts the readability of code, etc...

This is the reason why Promises are introduced.


Let's Understand how to write a Promise.

We have to create an instace of Promise class; Let's see how we can do that;


let p = new Promise((resolve, reject) =>{
  // your code
})

Enter fullscreen mode Exit fullscreen mode

The constructor function takes an executor function which takes two arguments which are resolve and reject repectively.

Whenever the promise is resolved successfully their after we call the resolve parameter with the result.

 function myPromise(){
   let p = new Promise((resolve, reject) =>{
     setTimeout(() => {
       resolve("promise is resolved :)")
     }, 2000);
   });
   return p;
 }
 myPromise();
Enter fullscreen mode Exit fullscreen mode

Whenever the promise is not resolved their after we call the reject parameter with a error.

 function myPromise(){
   let p = new Promise((resolve, reject) =>{
     setTimeout(() => {
       reject("promise failed to resolved :(")
     }, 2000);
   });
   return p;
 }
 myPromise();
Enter fullscreen mode Exit fullscreen mode

Now to get the output of the promise their are 2 methods:

  1. .then syntax
  2. async/await syntax

let me tell you that async/await syntax is most prefered way to get the output of a Promise.

Let's us see how we can get the output using .then method


function myPromise(){
  return new Promise((resolve, reject) =>{
    setTimeout(() =>{
      resolve("promise resolved :)")
    },2000)
  })
}

myPromise().then((data) =>{
  console.log(data);
})

Enter fullscreen mode Exit fullscreen mode
//output: 
  promise resolved :)
Enter fullscreen mode Exit fullscreen mode

Here, while using this method .then takes a callback function which recives the data that is returned by the function. Inside that function we can perform any operation which is required by the program.

Let's us see how we can get the output using async/await method


function myPromise(){
  return new Promise((resolve, reject) =>{
    setTimeout(() =>{
      resolve("promise resolved :)")
    },2000)
  })
}

async function getResult(){
  const data = await myPromise();
  console.log(data);
}


getResult();
Enter fullscreen mode Exit fullscreen mode
//output: 
  promise resolved :)
Enter fullscreen mode Exit fullscreen mode

Here, while using async/await syntax we initialise an async function inside which we call the myPromise function with await keyword before it.

This await keywords stops the excetion further down the line until the promise is not resolved.

As soon as the promise is resolved we can perform any operation which we want, in this case we have logged the data.


How errors are handled in promises

Both .then and async/await have different syntax for error handling.

First let's dig into .then() syntax.

Whenever we use .then() to consume the output of the promise we use .catch to handle the error.

Let's see how?


// refering to above example
// if promise is rejected then .catch catches the error

myPromise().then((data) =>{
   console.log(data);
}).catch((error)=>{
  // whatever errror we get

  console.log(error);
})

Enter fullscreen mode Exit fullscreen mode

using this way the error is handle.

But when we use Async/Await we write our code inside something known as try/catch block


// refering to above example

const consumer = async () => {
  try{
    const data = await myPromise();
    console.log(data);
  } catch(error){
    // whatever errror we get
    console.log(error);
  }
}

consumer();
Enter fullscreen mode Exit fullscreen mode

Advantage over Promises over Callbacks

  • Better Readability: Promises are a more structured and readable way of handling asynchronous operations. Promises allow you to chain multiple asynchronous operations together, Which makes the code more concise and clean as well as less bug-prone.
  • CallBack Hell: Promises help you to avoid callback hell, which occurs when you have nested callbacks that can become more difficult to find out bugs and manage.
  • Error Handling: Promises have built-in error-handling mechanisms to allow you to handle errors more easily.
  • Value return Ability: Promises allow you to return a value from an asynchronous operation, That can be helpful when you try to pass data from one asynchronous to another.

Hope this article clears the topic.
If you like this article then please like, share it on socials, comment your thoughts.

Top comments (0)