DEV Community

Cover image for Callbacks vs Promises vs Async/Await: The Ultimate Guide to Asynchronous Javascript
Shubham Tiwari
Shubham Tiwari

Posted on

57 4 4 5 6

Callbacks vs Promises vs Async/Await: The Ultimate Guide to Asynchronous Javascript

Learn JavaScript asynchronous programming with callbacks, promises, and async/await. Avoid callback hell and write cleaner, maintainable code with real-world examples.

Hello my fellow frontend developers, today i will be discussing one of the most important concept in javascript, asynchronous programming.

  • I'll be creating my code snippets on Scribbler.live first, which is a fantastic platform that allows you to run a JavaScript Notebook, Online Compiler, and Editor without the need for manual setup.
  • Additionally, I am including a link to code snippets that includes all of the code examples so you can open the snippet and run it yourself to see the results.
  • I will be using scrib.show from scribbler.live, it is equivalent to console.log

Let's dive in

Table of contents

What are callbacks?

  • A callback is a function passed as an argument to another function, which is then executed later (synchronously or asynchronously).
  • Callbacks are fundamental in JavaScript for handling asynchronous operations, event listeners, and functional programming.

Sample codes

Basic example

function saveOrder(orderId, userMail, callback) {
    const order = {
      orderId,
      userMail
    }
    scrib.show(order)
    callback(userMail); // Execute the callback function
}

function sendOrderEmail(userMail) {
    scrib.show(userMail, "Order confirmed")
}

saveOrder("145908275","user@gmail.com", sendOrderEmail);
// Will log the order and sends a confirmation email to the user using sendOrderEmail
Enter fullscreen mode Exit fullscreen mode

Asynchronous callback

  • We could take the example of setTimeout method as it executes asynchronously
console.log("Javascript is awesome");

setTimeout(() => {
    console.log("This codeblock runs after 2 seconds");
}, 2000);

console.log("Scribbler is awesome");

// Output
Javascript is awesome
Scribbler is awesome
This codeblock runs after 2 seconds
Enter fullscreen mode Exit fullscreen mode

Callback Hell (Pyramid of Doom)

function saveOrder(orderId, userMail, callback) {
    const order = {
      orderId,
      userMail
    }
    scrib.show(order)
    callback(userMail); // Execute the callback function
}

function sendDetailsToSeller(orderId, userMail, callback) {
  const details = {
      orderId,
      userMail
    }
    scrib.show(details)
    callback(userMail);
}

function sendOrderEmail(userMail) {
    scrib.show(userMail, "Order confirmed")
}

saveOrder("145908275","user@gmail.com", () => sendDetailsToSeller("145908275","user@gmail.com",sendOrderEmail));
 // If you see we have to pass down the callbacks at multiple level in order to perform chaining operations.
Enter fullscreen mode Exit fullscreen mode
  • When callbacks nest too deeply, they become difficult to read and maintain.

🔴 Problem: Difficult to read, maintain, and debug.
✅ Solution: Promises or Async/Await.

Promises - our saviour from callback hell

  • A Promise in JavaScript represents the eventual success or failure of an asynchronous operation. It provides a cleaner way to handle async tasks compared to traditional callbacks.
  • It takes a function with two parameters:
    • resolve → Call this when the async task is successful.
    • reject → Call this when the async task fails.
  • Promise States: A promise can be in one of three states:
    • Pending → Initial state, neither fulfilled nor rejected.
    • Fulfilled → The operation was successful (resolve() was called).
    • Rejected → The operation failed (reject() was called).

Sample codes

Basic example

const basicPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    let success = true; // Simulating success or failure
    if (success) {
      resolve("Task completed successfully! ✅");
    } else {
      reject("Task failed ❌");
    }
  }, 2000);
});

basicPromise
  .then((result) => {
    scrib.show(result); // Output: Task completed successfully! ✅
  })
  .catch((error) => {
    scrib.show(error); // Output: Task failed ❌ (if failed)
  })
  .finally(() => {
    scrib.show("Promise execution completed.");
  });
Enter fullscreen mode Exit fullscreen mode

Data fetching with fetch method

fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(response => {
        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }
        return response.json();
    })
    .then(data => console.log(data))
    .catch(error => console.log("Fetch Error:", error));
Enter fullscreen mode Exit fullscreen mode

Chaining promises methods

// Chaining promises method
new Promise((resolve) => {
  setTimeout(() => resolve(10), 1000);
})
  .then((num) => {
    scrib.show(num); // 10
    return num * 2;
  })
  .then((num) => {
    scrib.show(num); // 20
    return num * 3;
  })
  .then((num) => {
    scrib.show(num); // 60
  });
Enter fullscreen mode Exit fullscreen mode

Promise methods

  • Promise.all - Waits for all promises to resolve. If any promise rejects, the entire result is rejected.
  • Promise.race - Returns the first settled (fulfilled/rejected) promise.
  • Promise.allSettled - Waits for all promises to settle (resolve or reject) and returns their statuses.
  • Promise.any - Returns the first fulfilled promise, ignoring rejections.
const promise1 = new Promise((resolve) => {
  setTimeout(() => resolve(Math.floor(3.14), 1000));
})
const promise2 = new Promise((resolve) => {
  setTimeout(() => resolve(Math.ceil(3.14), 1000));
})
const promise3 = new Promise((resolve) => {
  setTimeout(() => resolve(Math.pow(3.14, 10), 1000));
})

// Promise.all
const promiseAll = Promise.all([promise1,promise2,promise3])
promiseAll.then(scrib.show())

// Promise.race
const promiseRace = Promise.race([promise1,promise2,promise3])
promiseRace.then(scrib.show())

// Promise.allSettled
const promiseAllSettled = Promise.allSettled([promise1,promise2,promise3])
promiseAllSettled.then(scrib.show())

// Promise.any
const promiseAny = Promise.any([promise1,promise2,promise3])
promiseAny.then(scrib.show())
Enter fullscreen mode Exit fullscreen mode
  • For even better async handling, async/await can be used, which simplifies working with promises.

Async/Await

  • async/await is a modern JavaScript feature that simplifies working with asynchronous code. It allows you to write asynchronous code in a synchronous-like manner, making it easier to read, understand, and debug.
  • Why Use async/await?
    • Before async/await, JavaScript developers handled asynchronous code using:
    • Callbacks (led to callback hell)
    • Promises (better, but still required .then() chaining)
  • How async/await works - Declaring a function as async makes it return a promise automatically even if it is returning a simple value.

Sample code

Solving callback hell issue

// Async await example for callback hell issue
async function saveOrder(orderId, userMail, callback) {
    const order = {
      orderId,
      userMail
    }
    scrib.show(order)
    return order
}

async function sendDetailsToSeller(orderId, userMail, callback) {
  const details = {
      orderId,
      userMail
    }
    scrib.show(details)
    return details
}

async function sendOrderEmail(userMail) {
    scrib.show(userMail, "Order confirmed")
    return "Email sent"
}

async function processOrder(orderId, userMail) {
  await saveOrder(orderId, userMail);
  await sendDetailsToSeller(orderId, userMail);
  await sendOrderEmail(userMail);
}

processOrder("145908275","user@gmail.com");
// Much readable and easy to manage
Enter fullscreen mode Exit fullscreen mode

Data fetching

// Fetching data using async/await
async function fetchData() {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); // ✅ Await fetch

  if (!response.ok) {
    throw new Error(`HTTP error! Status: ${response.status}`);
  }

  const data = await response.json(); // ✅ Await response.json()
  return data;
}

fetchData()
  .then(data => scrib.show("Fetched Data:", data))
  .catch(error =>  scrib.show("Error:", error));
Enter fullscreen mode Exit fullscreen mode

Combining loops with async/await for readable streams

// Combining async/await with loops
async function fetchTodos() {
  let urls = [
    "https://jsonplaceholder.typicode.com/todos/1",
    "https://jsonplaceholder.typicode.com/todos/2",
    "https://jsonplaceholder.typicode.com/todos/3"
  ];

  for (let url of urls) {
    let response = await fetch(url);
    let data = await response.json();
    scrib.show(data);
  }
}

fetchTodos();
Enter fullscreen mode Exit fullscreen mode

Checkout this embed to run the code example mentioned above

That's it for this post, Let me know if i could do any improvements in this article. Also, do check Scribbler.live website.

You can contact me on -

Instagram - https://www.instagram.com/supremacism__shubh/
LinkedIn - https://www.linkedin.com/in/shubham-tiwari-b7544b193/
Email - shubhmtiwri00@gmail.com

You can help me with some donation at the link below Thank you👇👇
https://www.buymeacoffee.com/waaduheck

Also check these posts as well




Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

Top comments (2)

Collapse
 
mahmoudalaskalany profile image
Mahmoud Alaskalany

This makes their understanding very smooth for mr thanks for that , please make one on closures

Collapse
 
gopisuvanam profile image
Gopi Krishna Suvanam

Nice article and very good examples.. makes the concept easy to understand

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay