Bitsy Explains
Home/JavaScript, Explained Like You're Five/Lesson 17

Callbacks in JavaScript

A callback is a function you hand to another function so it can call you back later. You already use them with map, filter and setTimeout.

Lesson 17 of 203 min readVideo
Video thumbnail: Callbacks Watch the 90-second video
Explain like I'm five

You order a pizza and give them your phone number. You don't stand at the door waiting. You go play. When the pizza is ready, they call you back. A callback is that phone number.

A function you hand over

A callback is a function you pass to another function. That other function decides when to call it.

function orderPizza(callMe) {
  // ...making pizza...
  callMe("Pizza's here!");
}
orderPizza(msg => console.log(msg)); // prints: Pizza's here!

orderPizza gets your function as callMe. When the pizza is done, it calls callMe and passes along a message. You hand over the phone number, and they call you back.

The name callMe is just a parameter name. Any name works, and you'll often see callback, cb or done.

You don't have to write the callback inline. You can pass a function that already has a name. Leave off the parentheses, because you are handing over the function, not calling it yet.

function sayThanks(msg) {
  console.log("Thanks! " + msg); // prints: Thanks! Pizza's here!
}
function orderPizza(callMe) {
  callMe("Pizza's here!");
}
orderPizza(sayThanks);

Inside orderPizza, callMe and sayThanks are the same function under two names.

You've used them already

The function you give to map or filter is a callback. map calls it once for each item.

const scores = [3, 8, 5];
const doubled = scores.map(n => n * 2);
console.log(doubled); // prints: [ 6, 16, 10 ]

setTimeout takes one too. It waits a number of milliseconds, then calls your function. Two thousand milliseconds is two seconds.

setTimeout(() => console.log("Ding"), 2000); // prints: Ding
console.log("Waiting..."); // prints: Waiting...

"Waiting..." prints first, and "Ding" two seconds later. JavaScript doesn't stand at the door. It keeps going and gets called back when the timer is done.

Some jobs take time, like a timer or a request to the internet. Callbacks let JavaScript carry on with other work instead of freezing until the job is finished.

The twist: callback hell

When one slow job needs the result of another, you put a callback inside a callback inside a callback.

function getUser(id, done) { setTimeout(() => done({ id, name: "Mia" }), 10); }
function getOrders(user, done) { setTimeout(() => done(["pizza"]), 10); }
getUser(1, user => {
  getOrders(user, orders => {
    console.log(user.name, orders); // prints: Mia [ 'pizza' ]
  });
});

With two steps it's fine. With four or five, the code drifts further and further to the right. This is called callback hell. It's hard to read and hard to fix, and you have to handle errors again at every level. Promises fix this, and they are the next lesson.

Another problem is trust. When you hand your callback to someone else's code, they decide when and how often to call it. A bug on their side could call it twice, or never.

When to use callbacks

  • Array methods like map, filter, forEach and sort.
  • Timers, with setTimeout and setInterval.
  • Events, such as "run this when the button is clicked".
  • Older Node.js functions, which often call back with (err, data), error first.

For a chain of slow steps, reach for promises or async and await instead.

Callbacks are not going away, though. Even promises and await are built on the same idea of handing over a function to be called later.

Rule of thumb

A callback is a function you hand over, for someone else to call.

Common mistake

Calling the function instead of handing it over. setTimeout(sayHi(), 1000) runs sayHi right away and passes its result. Pass the function itself with setTimeout(sayHi, 1000), or wrap it as () => sayHi().

Check yourself

In setTimeout(() => console.log("Ding"), 2000), which part is the callback?