Callback Hell & Async Patterns

Difficulty: Intermediate

Question

What is callback hell? How do Promises and async/await solve it?

Answer

Callback hell (also called the "pyramid of doom") occurs when multiple asynchronous operations are nested inside callbacks, making the code deeply indented and hard to read.

Evolution of async patterns: 1. Callbacks: Simple but leads to nesting and error handling issues 2. Promises: Chain .then() calls, centralized error handling with .catch() 3. Async/await: Synchronous-looking code, try/catch for errors

Async/await is syntactic sugar over Promises and is the preferred modern approach.

Code examples

Callback Hell vs Promise Chain

// Callback Hell (the pyramid of doom)
getUser(id, function(err, user) {
  if (err) return handleError(err);
  getOrders(user.id, function(err, orders) {
    if (err) return handleError(err);
    getDetails(orders[0].id, function(err, details) {
      if (err) return handleError(err);
      console.log(details);
    });
  });
});

// Promise chain (flat)
getUser(id)
  .then(user => getOrders(user.id))
  .then(orders => getDetails(orders[0].id))
  .then(details => console.log(details))
  .catch(handleError);

Promises flatten the nesting. Each .then() returns a new Promise, and a single .catch() handles all errors.

Async/Await Solution

async function getShippingInfo(userId) {
  try {
    const user = await getUser(userId);
    const orders = await getOrders(user.id);
    const details = await getDetails(orders[0].id);
    console.log(details);
    return details;
  } catch (err) {
    handleError(err);
  }
}

// Parallel execution when operations are independent
async function loadDashboard() {
  const [user, settings, notifications] = await Promise.all([
    fetchUser(),
    fetchSettings(),
    fetchNotifications()
  ]);
  console.log(user, settings, notifications);
}

Async/await reads like synchronous code. Use Promise.all for independent parallel operations.

Key points

Concepts covered

Callbacks, Callback Hell, Promises, Async/Await