DEV Community

Cover image for Callback vs Promises vs Async Await
Mrityunjaya Prajapati
Mrityunjaya Prajapati

Posted on

Callback vs Promises vs Async Await

In this blog I will explains the fundamental concepts that JavaScript relies on to handle asynchronous operations. These concepts include Callback functions, Promises and the use of Async, and Await to handle different operations in JavaScript.

Before jumping to comparison between the three, let's get a brief understanding of synchronous (blocking) and asynchronous(non-blocking).

Difference Between Sync and Async

To make it easy to understand, let’s take a real life example which probably will explain the difference between asynchronous and synchronous.

Imagine we go to a restaurant, a waiter comes to a table, takes your order and gives it to the kitchen. Lets divided whole process into the steps

  • waiter takes order from table 1
  • waiter inform chef for the dishes
  • serve dishes to table 2 OR take order from table 2

The table has to wait for the chef to cook one meal before they serve another table. This is what we called asynchronous or non-blocking architecture. Here the waiter is like a thread allocated to handle requests. So a single thread is used to handle multiple requests.

In contrast to non-blocking or asynchronous architecture, we have blocking or synchronous architecture. Let's see how that works. So back to the restaurant example, imagine you go to another restaurant and in this restaurant, a waiter is allocated to you. He takes your order and gives it to the kitchen. Now he is sitting in the kitchen waiting for the chef to prepare your meal and this time he is not doing anything else he is just waiting for he is not going to take any order from another table until your meal is ready. This is what we called synchronous or blocking architecture.

Now, the first restaurant example represents an asynchronous process because you did not have to wait, the waiter takes the order from one table and goes to the next table to take the order. While the second example restaurant represents a synchronous operation because you had to wait until the resource (waiter in this case) can proceed with you. This is the single, most fundamental difference between sync and async processes.

There are different ways to handle the async code in JavaScript. Those are callbacks, promises, and async/await.

Callbacks:

In JavaScript, functions are objects, so we can pass objects to functions as parameters.

Let's take an example of callback function:

function printString(){
   console.log("Jay"); 
   setTimeout(function()  { console.log("Jeevan"); }, 500); 
   console.log("Joy")
}

printString();
Enter fullscreen mode Exit fullscreen mode

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

Jay
Jeevan
Joy
Enter fullscreen mode Exit fullscreen mode

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

Jay
Joy
Jeevan
Enter fullscreen mode Exit fullscreen mode

There is a built-in method in JavaScript called “setTimeout”, which calls a function after a given period of time (in milliseconds).

In other words, the message function is being called after something happened (after 5 seconds passed for this example), but not before.

Promises:

A promise in JavaScript you can consider as a promise in real life. When we make a promise in real life, it means we are going to do something in the future because promises can only be made for the future.

A promise has two possible outcomes: either you will keep your promise or not.

Same fundamentals of promises applies in JavaScript. When we define a promise in JavaScript, it will be resolved when the time comes, or it will get rejected.

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:

First of all, a 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

For example, when we request data from the server by using a Promise, it will be in pending state until we receive our data.

If we get the information from the server, then Promise will be resolved successfully but if we don’t get the information, then the Promise will be in the rejected state.

Creating a Promise:

Firstly, we use a constructor to create a Promise object. The promise has two parameters, one for success (resolve) and one for fail (reject):

const myFirstPromise = new Promise((resolve, reject) => { 
    const condition = true;   
    if(condition) {
         setTimeout(function(){
             resolve("Promise is resolved!"); 
        }, 500);
    } 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:

To use the above 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

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")
}
Enter fullscreen mode Exit fullscreen mode

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 this is necessary also if you want to use Await. We can say that 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.

Let's take an example to understand the Async and Await with our demoPromise:

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

    resolve(message)
  });
}


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

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

demoPromise();
Enter fullscreen mode Exit fullscreen mode

Conclusion

Based on our use case we can prefer any one of this approach. Since async/await is wrapped on top of Promise, all of the Promise related functionalities are supported within it. So when comparing callback with Promise, Promise has move advantages than callback. Listing out few of them;

  1. Single Error Propagation using catch block
  2. Overcome callback hell using Promise Chaining or async/await/
  3. Implement parallel processing using Promise.all().
  4. Promise supports few other static methods like (race(), allSettled() and any()) which shall be very usefully on need basic.

Hope this story would have helped you in refreshing the asynchronous handling concepts in Javascript. Please feel free to share your comments, suggestions or queries.

Oldest comments (1)

Collapse
 
cbuschka profile image
Info Comment hidden by post author - thread only accessible via permalink
Cornelius Buschka

Some comments have been hidden by the post's author - find out more