Skip to the solution
MediumDSA6 min read

Explain what 'callback hell' is and how to avoid it.

207 views

The problem

Question presented to candidate: "You are reviewing a pull request with five levels of nested callbacks, each indented further than the last. What is this pattern called, why is it a problem, and what would you ask the author to change?"

What a strong answer should cover:

  • πŸ“Œ Callback hell (also called the "pyramid of doom") is the deep, rightward-drifting nesting that results from chaining several asynchronous steps using plain callbacks, where each step's callback is defined inside the previous one.
  • The problem is not merely aesthetic indentation β€” it is that nested callbacks make error handling, control flow, and variable scoping all harder to follow: there is no single place to catch an error from any step, and reasoning about "what runs after what" requires mentally tracing the nesting.
  • Promises flatten the nesting into a chain (.then().then().then()) with a single .catch() for errors from any step, at the cost of still reading somewhat differently from synchronous code.
  • async/await flattens it further into code that reads like ordinary synchronous, top-to-bottom logic, with a normal try/catch around the whole sequence β€” this is the modern default recommendation.
  • The three approaches are not different capabilities, only different shapes of the same underlying async behavior β€” a good answer proves this rather than asserting it, showing that a nested-callback version, a Promise-chain version, and an async/await version of the same 3-step sequence produce identical results.
  • Callback hell is not inherent to using callbacks at all β€” a single callback, or even two independent (non-nested) callbacks, is not a problem. The issue specifically arises from serial dependency chains expressed through nesting rather than composition.

Clarifying questions expected:

  • "Are these steps genuinely sequential (each depends on the previous result), or could some run in parallel?" β€” a parallel case calls for Promise.all, not just flattened sequential awaits.
  • "Is this in a codebase that can adopt async/await, or does it need to stay compatible with an older callback-based API?" β€” decides whether promisifying the underlying API is part of the fix.

Code / implementation expected: Yes β€” the same 3-step sequence written all three ways, with confirmation they behave identically, is the clearest demonstration.

  • callbacks
  • asynchronous
  • promises
  • async/await

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 8 sections

Read the code

JavaScriptThe same 3-step sequence as nested callbacks, a Promise chain, and async/await β€” verified identical output
function wait(ms, label) { return new Promise((r) => setTimeout(() => r(label), ms)); }

// Nested callbacks (the anti-pattern):
function callbackStyle(cb) {
  setTimeout(() => {
    setTimeout(() => {
      setTimeout(() => cb(null, "done via nested callbacks"), 10);
    }, 10);
  }, 10);
}

// A Promise chain:
function promiseStyle() {
  return wait(10).then(() => wait(10)).then(() => wait(10)).then(() => "done via .then chain");
}

// async/await:
async function asyncStyle() {
  await wait(10);
  await wait(10);
  await wait(10);
  return "done via async/await";
}

// All three, run back to back:
new Promise((resolve) => callbackStyle((e, r) => { console.log(r); resolve(); }))
  .then(() => promiseStyle().then(console.log))
  .then(() => asyncStyle().then(console.log));
// done via nested callbacks
// done via .then chain
// done via async/await

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 105 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextMedium

What is the difference between operational and programmer errors in Node.js?

Continue