Handle asynchronous calls using callback vs promise vs async-await

Handle asynchronous calls using callback vs promise vs async-await

We use Callback(), Promises and Async-Await to handle the asynchronous operations in JavaScript. Now, what is an asynchronous operation? Asynchronous operation means any process that can happen independently and takes longer time to execute, which usually run alongside other synchronous operation and completes in the future. Let's take a scenario and try to solve with Callback(), Promises and Async-Await.

const getFruit = () => {
  setTimeout(() => {
    return { name: 'Apple' }
  }, 1000)

const fruit = getFruit() // This doesn't return any result
console.log(fruit.name) // This won't work

This won't output anything. Because, JavaScript works synchronously by default, but here we have used setTimeout() which is an asynchronous operation, it would take 1 sec (as mentioned 1000 ms in the code) to execute.

Synchronous operation in JavaScript means it executes code from top to bottom. It read each step one by one but before executing the current step the previous step must be completed, no matter how long it would take to complete.

You may also like: Most Useful JavaScript Array Functions

On the other hand, the asynchronous operation works differently. The asynchronous operation usually runs alongside with other synchronous operations, can take a longer time to execute and completes in the future.

Solution 1: Use Callback()

Callback is the oldest way to handle asynchronous operation. We used it before Promise and Async-Await released.

A Callback is a simple function that we passed to another function as a parameter value and will only be executed when it will be called inside of that function.

Revised code of the above example using Callback():

const getFruit = (cb) => {
  setTimeout(() => {
    cb({ name: 'Apple' })
  }, 1000)

getFruit(fruit => {
  console.log(fruit.name) // This will print 'Apple' after 1 second

Problem with using Callback():

Callback is very useful for simple and small asynchronous operations. But when working with a complex and nested scenario this is not considered best practice. Add callback in every level of nesting, codes getting more complex and it lost its readability and debugging becomes harder.

Solution 2: use Promise()

Promise introduced in ES6 to handle asynchronous operations in JavaScript without making too many callbacks. A Promise in JS is similar to our real-life promise. If we do a promise to someone that means we are going to do something in the future which is guaranteed.

A Promise is an object which has 3 possible states:

1.Pending State: When a Promise has been initiated it is in a pending state until it gets Resolved or Rejected.

2.Resolved / Fulfilled State: When a Promise is successfully completed it return Resolved state.

3.Rejected State: It means Promise did not complete and return an error.

How a Promise can be created:

A Promise can be created by using the new keyword and Promise constructor which has a resolve and a reject callback. If Promise is successful call resolve(), if not call reject(). Let's create a simple Promise:

let completed = true;

let myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        if (completed) {
            resolve("Promise was successful");
        } else {
            reject("Promise was failed");
    }, 3000);

After 3 seconds you will see the state of the promise is resolved and return a message "Promise was successful".

You may also like: How to use Local Storage

Now, if we write our example using Promise the revised code would look like:

const getFruit = () => {
  return new Promise(resolve => {
    setTimeout(() => {
        resolve({ name: 'Apple' })
    }, 1000);
    .then(fruit => {
      console.log(fruit.name) // This will print 'Apple' after 1 second
    .catch(error => {
        // handle error here

Solution 3: use Async-Await()

An async function is a function declared with the "async" keyword and always returns a promise which will be either resolved or rejected. The "await" keyword is used to make JavaScript wait until the Promise returns a result. Await can be used inside an Async block only.

So, Async-Await functions are a combination of promises and generators in ES6.

Let's write our above example using Async-Await:

async function getFavFruit() {
    console.log('Start Fetching');
    let promise = new Promise((res, rej) => {
        setTimeout(() => res({ name: 'Apple' }), 1000)

    // wait until the promise returns us a value
    let result = await promise;   

    console.log('My favourite fruit is: ', result.name);
    console.log('End of Fetching');


You can see we have used await while doing an asynchronous operation and it takes 1 second to get the response, so till that the execution will be halted and resumes later. The log order from the above function as follows:

Start Fetching
My favourite fruit is: Apple     // wait for 1 second
End of Fetching

Hope it helps.

You may also like: JavaScript null vs undefined vs blank vs value

Related Articles

Leave a Reply