What is Callback Hell in JavaScript?


Callback Hell (also known as the Pyramid of Doom) happens when multiple asynchronous functions are nested inside each other, making the code hard to read, maintain, and debug.



Problem: Nested Callbacks (Callback Hell)

When you use many setTimeout, fetch, or custom async operations inside each other, your code can become deeply indented and confusing.

Example of Callback Hell:

setTimeout(() => {
  console.log("1st task done");
  setTimeout(() => {
    console.log("2nd task done");
    setTimeout(() => {
      console.log("3rd task done");
    }, 1000);
  }, 1000);
}, 1000);

Output

1st task done
2nd task done
3rd task done

This is hard to read, hard to debug, and grows worse with more logic.



Why Callback Hell Happens

  • JavaScript is single-threaded and uses asynchronous programming to handle slow tasks (like API calls).
  • Callbacks are a way to run code after an async task is complete.
  • Too many nested callbacks = messy code.


How to Avoid Callback Hell


1. Use Named Functions

Refactor nested callbacks into separate, named functions:

function task1(callback) {
  setTimeout(() => {
    console.log("Task 1");
    callback();
  }, 1000);
}

function task2(callback) {
  setTimeout(() => {
    console.log("Task 2");
    callback();
  }, 1000);
}

function task3() {
  setTimeout(() => {
    console.log("Task 3");
  }, 1000);
}

task1(() => {
  task2(() => {
    task3();
  });
});


2. Use Promises

Promises let you chain async tasks cleanly.

function task(ms, msg) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(msg);
      resolve();
    }, ms);
  });
}

task(1000, "Task 1")
  .then(() => task(1000, "Task 2"))
  .then(() => task(1000, "Task 3"));


3. Use async/await (Best Modern Solution)

Async/Await allows writing asynchronous code that looks like synchronous code.

function task(ms, msg) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(msg);
      resolve();
    }, ms);
  });
}

async function runTasks() {
  await task(1000, "Task 1");
  await task(1000, "Task 2");
  await task(1000, "Task 3");
}

runTasks();