JS Sketchbooksee JavaScript think ✏️
← back to phase 6

6.3 — Callbacks & callback hell

You now know async results arrive later — so how does later-code get the value? The original answer, powering a decade of JavaScript: pass a callback — "when you're done, call this function with the result." It's 3.8's higher-order functions, doing the heaviest lifting of their careers.

It works. And then real life asks for step after step — load the user, then their cart, then pay — and the pattern's flaw appears: each next step must nest inside the previous callback, and the code slides rightward into the pyramid of doom. Today you build the pyramid with your own hands and feel exactly why promises had to be invented.

watch it happen
function loadUser(cb) {
  setTimeout(() => cb("vas"), 40);
}
function loadCart(user, cb) {
  setTimeout(() => cb(user + "'s cart"), 40);
}
function pay(cart, cb) {
  setTimeout(() => cb(cart + " paid"), 40);
}

loadUser((user) => {
  loadCart(user, (cart) => {
    pay(cart, (receipt) => {
      console.log(receipt);
    });
  });
});

The three helpers all follow the callback contract: do async work (here a small timer stands in for the network), and when done, call cb(result).

watch the left margin — the pyramid of doomloadUser(cb)…nesting:the three helpers all follow the callback contract: do async work, andwhen done, call cb(result)(console: nothing yet)
under the hood

The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”

The pattern's proper name is continuation-passing. Instead of returning a value, a function receives "the rest of the program" (the continuation) as an argument. It calls it when ready. Every callback API you'll ever meet — timers, old Node file APIs, event listeners in Phase 7 — is this one idea.

Be precise about what's broken. ONE level of callback is completely fine and stays common everywhere — every addEventListener, every test('…', fn) in your Phase 10 future is a callback. The failure mode is sequencing chains of dependent async steps — that's where nesting compounds, errors scatter, and reading order stops matching running order.

The cure's shape is worth previewing precisely. Promises turn "hand your next step INTO the function" into "the function HANDS BACK an object you can attach next steps to." Return-a-thing beats take-a-thing — it flattens the pyramid into a chain and gives errors one shared drain. That object is lesson 6.4.

your turn

⌨️ a two-step relay, callback style

Feel the pattern (and its shape) in your own hands: two async steps where the second NEEDS the first’s result — so it must live inside the first’s callback.

requirements:

  • A function fetchName(cb) that waits 30ms (setTimeout), then calls cb("vasavi").
  • A function makeBadge(name, cb) that waits 30ms, then calls cb("badge: " + name).
  • Wire the relay: fetch the name, and INSIDE its callback, make the badge from it, and inside THAT callback print the result. One print, two levels deep — feel the slide.

when you press RUN, the console must show exactly:

badge: vasavi

✏️ Quick check 1

Where does the value "vas" exist? Type: inside or outside (the callback).

function loadUser(cb) {
  setTimeout(() => cb("vas"), 40);
}
loadUser((user) => {
  // here?
});
// or here?

✏️ Quick check 2

Step B needs step A’s result. In callback style, where must B’s call be written? Type: inside or after (A’s callback).

✏️ Quick check 3

Can a try/catch wrapped around loadUser(…) catch an error thrown LATER inside its callback? Type yes or no.

teach it back

🗣️ Now teach it back

Explain to a friend how async code gets its results using callbacks, why chaining dependent steps creates the pyramid of doom, and why error handling makes it even worse.

Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.

a few sentences, minimum — you’ve got this
to remember
Callback contract: no return — the result arrives INSIDE the callback, later. (3.8, promoted to async duty.)
Dependent steps must nest inside each other’s callbacks → the pyramid of doom. Structure, not style.
try/catch can’t reach later stack turns → per-level error checks, scattered. The cure (an object you attach steps to) is next.
next: 6.4 Promises →