Callbacks and Asynchronous Thinking
This chapter introduces callbacks — functions you hand to other code so it can call you back later. It also introduces the idea of asynchronous (async) work: things that finish at an unknown time, like loading a web page or waiting for a click.
Why "later" is a problem
Most code you have written so far runs top to bottom. Line 1 finishes, then line 2 runs. This is called synchronous code.
But browsers do many things that take time: downloading data, waiting for a user to click, running a timer. JavaScript cannot just pause and wait — it would freeze the whole page. Instead, it starts the task and moves on. When the task finishes, the browser calls a function you provided. That function is the callback.
A callback is a function passed as an argument to another function, to be run later.
You have already used callbacks without the name:
// The arrow function () => { ... } is a callback.
// The browser calls it when the button is clicked.
button.addEventListener('click', () => {
console.log('You clicked me!');
});Here addEventListener takes your callback and stores it. JavaScript runs it only when the click happens — maybe never.
Timers: a simple async example
console.log('Start');
setTimeout(() => {
console.log('Two seconds later');
}, 2000);
console.log('End');setTimeout waits 2000 milliseconds (2 seconds), then calls your callback. Run this and the output is:
StartEndTwo seconds later
The timer did not block the rest of the program. JavaScript moved on immediately. This ordering surprises beginners — but it is the heart of async thinking: code you write does not always run in the order it appears.
Why callbacks exist
Callbacks let you say "do this when X happens" without knowing when (or whether) X happens. Common uses:
- Events: run code on a click, keypress, or page load.
- Timers: run code after a delay, or repeatedly with
setInterval. - Data loading: run code when data arrives from a server.
function repeatThreeTimes(fn) {
for (let i = 0; i < 3; i++) {
fn(i);
}
}
repeatThreeTimes((n) => console.log(`Round ${n}`));
// Round 0, Round 1, Round 2Here repeatThreeTimes is a normal function that accepts a callback. Callbacks are just functions — nothing special is needed to pass them around.
The catch: callback nesting
When one async task depends on another, callbacks pile up:
setTimeout(() => {
console.log('step 1 done');
setTimeout(() => {
console.log('step 2 done');
setTimeout(() => {
console.log('step 3 done');
}, 1000);
}, 1000);
}, 1000);Each step lives inside the previous one. With more steps this becomes hard to read — developers call it "callback hell". It works, but it is messy.
The two later chapters, Promises and async/await, exist to fix exactly this problem. Callbacks are still the foundation: promises and async/await are cleaner ways to manage the same "call me back later" idea.
callout tip If a function argument is a function, it is a callback. Ask yourself: who calls this, and when? That question untangles almost all async code.
diagram mermaid caption: Synchronous code versus asynchronous code over time alt: A timeline showing synchronous code running steps in order and pausing, versus asynchronous code starting a timer, continuing to other work, and running the callback when the timer finishes. mermaid sequenceDiagram participant JS as JavaScript participant Timer as Browser Timer Note over JS: Synchronous JS->>JS: line 1 JS->>JS: line 2 (waits to finish) Note over JS,Timer: Asynchronous JS->>Timer: setTimeout(callback, 2000) JS->>JS: next lines run immediately Timer-->>JS: 2 seconds pass JS->>JS: callback runs now
exercise Q: In what order do the messages print: console.log('A'); setTimeout(() => console.log('B'), 100); console.log('C'); A: A, then C, then B. The timer does not block; the callback runs after the rest of the code, roughly 100 milliseconds later.
exercise Q: What is a callback in one sentence? A: A function passed to another function as an argument, which that function calls later — for example when a click happens or a timer finishes.
estimate minutes: 30
Spotted a mistake, or code that does not work?