A Function You Pass For Later
JavaScript Callbacks
A callback is a function you hand to someone else, to call later. The original pattern for async — and still everywhere in browser APIs.
What you'll learn
- Recognize the callback pattern
- Pass functions to `setTimeout` and event handlers
- See why nested callbacks ("callback hell") motivated Promises
A callback is a function you pass as an argument, to be called later — often after something async finishes. It’s the oldest async pattern in JavaScript, predating Promises by a decade.
Synchronous Callbacks
You’ve been writing these since the iteration lessons. map, filter,
reduce, forEach, find all take a callback that runs once per
item:
[1, 2, 3].forEach((n) => console.log(n));
const doubled = [1, 2, 3].map((n) => n * 2);
console.log(doubled); Asynchronous Callbacks
The other use: hand a function to a feature that will call it back once something finishes — a timer, an event, a network response.
console.log("before");
setTimeout(() => {
console.log("inside callback (after 0ms)");
}, 0);
console.log("after");
// Logs:
// "before"
// "after" ← synchronous code runs first
// "inside callback…" Notice the order. setTimeout schedules the callback, then the
script keeps running. The callback fires later, even when the delay
is zero.
Event Handlers
A click handler is a callback:
const btn = document.querySelector("#go");
btn.addEventListener("click", (event) => {
console.log("clicked!", event.target);
}); You hand addEventListener a function. The browser calls it back
whenever the user clicks.
Callback Hell
Before Promises, async code often nested callbacks. Each step waited for the previous one.
loadUser((user) => {
loadOrders(user.id, (orders) => {
sortOrders(orders, (sorted) => {
render(sorted);
});
});
}); This is callback hell — deep nesting, fragile error handling,
hard to read. Promises and async/await solved this.
Callback-Style Error Handling
A common convention in Node-style APIs: the first parameter of the
callback is an error (or null if everything’s fine).
import { readFile } from "fs";
readFile("data.txt", "utf-8", (err, contents) => {
if (err) {
console.error("failed:", err);
return;
}
console.log("read:", contents);
}); You have to remember the convention and check for the error every
time. Promises and async/await make this cleaner — the rest of
this chapter covers them.
Callbacks Aren’t Going Away
Even with Promises, you’ll still pass callbacks to:
setTimeout/setIntervaladdEventListener(events)- Array methods (
map,filter, etc.) - Many library APIs
What changed is that new async APIs almost always use Promises instead of error-first callbacks.
Up Next
A better abstraction for async work: Promises.
JavaScript Promises →