DEV Community

Cover image for Callback vs Promises vs Async Await
Neeraj Kumar
Neeraj Kumar

Posted on • Updated on

Callback vs Promises vs Async Await

Callbacks:
In JavaScript, functions are objects. So we can pass objects to functions as parameters.

We can also pass functions as parameters to other functions and call them inside the outer functions. So callback is a function that is passed to another function. When the first function is done, it will run the second function.

example of callback function:

function callbackData(){
   console.log("Neeraj"); 
   setTimeout(function()  { console.log("Kumar"); }, 400); 
  console.log("Yadav")
}

callbackData();
Enter fullscreen mode Exit fullscreen mode

If that were sync code, we would have encountered the following output.

Neeraj
Kumar
Yadav
Enter fullscreen mode Exit fullscreen mode

But the setTimeout is an async function then the output of the above code will be:

Neeraj
Yadav
Kumar
Enter fullscreen mode Exit fullscreen mode

There is a built-in method in JavaScript called setTimeout, which calls a function or evaluates an expression after a given period of time (in milliseconds).

In other words, the message function is being called after something happened (after 4 seconds passed for this example), but not before. So the callback is the function that is passed as the argument to setTimeout.

Callback as an Arrow Function:
callback function as an ES6 arrow function, which is a newer type of function in JavaScript:

function callbackData(){
   console.log("Neeraj"); 
   setTimeout(()=>  { console.log("Kumar"); }, 400); 
  console.log("Yadav")
}

callbackData();
Enter fullscreen mode Exit fullscreen mode

The problem with callbacks is it creates something called Callback Hell. Basically, you start nesting functions within functions within functions, and it starts to get really hard to read the code. So in this situation Promises came to handle the nested callback in a better way.

Promises:
A promise in JavaScript is similar to a promise in real life. When we make a promise in real life, it is a guarantee that we are going to do something in the future. Because promises can only be made for the future.

A promise has two possible outcomes: it will either be kept when the time comes, or it won’t.

This is also the same for promises in JavaScript. When we define a promise in JavaScript, it will be resolved when the time comes, or it will get rejected. It sounds like the IF condition. But there are huge differences between them.

A promise is used to handle the asynchronous result of an operation. JavaScript is designed to not wait for an asynchronous block of code to completely execute before other synchronous parts of the code can run. With Promises, we can defer the execution of a code block until an async request is completed. This way, other operations can keep running without interruption.

States of Promises:
Promise is an object. There are 3 states of the Promise object:

  • Pending: Initial State, before the Promise succeeds or fails.
  • Resolved: Completed Promise
  • Rejected: Failed Promise, throw an error

Creating a Promise:

const myPromise = new Promise((resolve, reject) => {  
    // condition
});
Enter fullscreen mode Exit fullscreen mode

Let’s create a promise:

const myFirstPromise = new Promise((resolve, reject) => { 
    const condition = true;   
    if(condition) {
         setTimeout(function(){
             resolve("Promise is resolved!"); // fulfilled
        }, 300);
    } else {    
        reject('Promise is rejected!');  
    }
});
Enter fullscreen mode Exit fullscreen mode

In the above Promise If Condition is true, resolve the promise returning the Promise is resolved , else return an error Promise is rejected. Now we have created our first Promise, Now let's use it.

Using Promise:

create Promise we use then() for resolve and catch() for reject.

myFirstPromise
.then((successMsg) => {
    console.log(successMsg);
})
.catch((errorMsg) => { 
    console.log(errorMsg);
});
Enter fullscreen mode Exit fullscreen mode

let's take this a step further:

const demoPromise= function() {
  myFirstPromise
  .then((successMsg) => {
      console.log("Success:" + successMsg);
  })
  .catch((errorMsg) => { 
      console.log("Error:" + errorMsg);
  })
}

demoPromise();
Enter fullscreen mode Exit fullscreen mode

In our created promise condition is true and we call demoPromise() then our console logs read:

Success: Promise is resolved!
Enter fullscreen mode Exit fullscreen mode

So if the promise gets rejected, it will jump to the catch() method and this time we will see a different message on the console.

Error: Promise is rejected!
Enter fullscreen mode Exit fullscreen mode

What is Chaining?

Sometimes we need to call multiple asynchronous requests, then after the first Promise is resolved (or rejected), a new process will start to which we can attach it directly by a method called chaining.
So we create another promise:

const helloPromise  = function() {
  return new Promise(function(resolve, reject) {
    const message = `Hi Neeraj, How are you!`;

    resolve(message)
  });
}
Enter fullscreen mode Exit fullscreen mode

We chain this promise to our earlier “myFirstPromise” operation like so:

const demoPromise= function() {

  myFirstPromise
  .then(helloPromise)
  .then((successMsg) => {
      console.log("Success:" + successMsg);
  })
  .catch((errorMsg) => { 
      console.log("Error:" + errorMsg);
  })
}

demoPromise();
Enter fullscreen mode Exit fullscreen mode

Async/Await:
Await is basically syntactic sugar for Promises. It makes your asynchronous code look more like synchronous/procedural code, which is easier for humans to understand.
Syntax of Async and Await:

async function printMyAsync(){
  await printString("one")
  await printString("two")
  await printString("three")
}
You can see that we use the `async` keyword for the wrapper function printMyAsync. This lets JavaScript know that we are using async/await syntax, and is necessary if you want to use Await. This means you can’t use Await at the global level. It always needs a wrapper function. Or we can say await is only used with an async function.

The await keyword is used in an async function to ensure that all promises returned in the async function are synchronized, ie. they wait for each other. Await eliminates the use of callbacks in .then() and .catch(). In using async and await, async is prepended when returning a promise, await is prepended when calling a promise. try and catch are also used to get the rejection value of an async function.

Enter fullscreen mode Exit fullscreen mode

example to understand the Async and Await with our demoPromise:

async function demoPromise() {
  try {
    let message = await myFirstPromise;
    let message  = await helloPromise();
    console.log(message);

  }catch((error) => { 
      console.log("Error:" + error.message);
  })
}

// finally, call our async function

(async () => { 
  await myDate();
})();

Enter fullscreen mode Exit fullscreen mode

Top comments (0)