Callbacks and the Pyramid of Doom

Freshman Year, page 3 of 10. A one-minute read.

Before 2015, the main way to handle asynchronous tasks was with callbacks: you pass a function that should be called when the slow task finishes. Let's pretend we're loading some data from a server. (We'll use setTimeout to fake the slowness.)

JavaScript
function getUser(id, callback) {
  setTimeout(() => {
    callback({ id: id, name: "Olivia" });
  }, 100);
}

getUser(1, (user) => {
  console.log(`Loaded user: ${user.name}`);
});
console.log("Loading...");

It prints

Loading...
Loaded user: Olivia

That works fine for one task. But real apps often need to do several slow things in order, where each step needs the result of the previous one. "Get the user, then get their orders, then get the items in the first order, then get the prices..." With callbacks, each step has to go inside the previous step's callback:

The Pyramid of Doom: callbacks inside callbacks inside callbacks

The code drifts further and further to the right, forming a sideways pyramid. Programmers call this callback hell or the Pyramid of Doom. It's hard to read, hard to change, and handling errors at every level is a nightmare.

There had to be a better way. And there is.

Reading is step one

CurlyScript turns these notes into a game: short questions, an explanation after every answer, and a test at the end of each grade. Free, with nothing to sign up for.

Start learning