A callback is a function you pass into another function as an argument, so that the receiving function can run it at the right moment instead of you calling it yourself. The name is literal: you give the function and the other code calls you back when it is ready. Callbacks are how programs react to things that happen later, like a button click, a timer firing, or a file finishing downloading. They are one of the most fundamental patterns in event-driven and asynchronous code, and once the idea clicks, a lot of modern programming stops looking like magic.
How a callback works
Functions in languages like JavaScript are values. That means you can store a function in a variable and pass it around just like a number or a string. A callback is simply a function used that way: handed to another function, which decides when to invoke it.
function greet(name) {
console.log("Hi " + name);
}
// pass greet as a callback, do not call it yourself
setTimeout(greet, 1000, "Sam");
// greet runs after about one second, called by setTimeout
Notice you wrote greet, not greet(). You are passing the function itself, not its result. The receiving function, setTimeout, holds onto it and calls it later. A function that accepts another function like this is called a higher-order function. If the wider vocabulary is fuzzy, the primer on what a parameter is helps, since a callback is just a parameter that happens to be a function.
Synchronous vs asynchronous callbacks
Not every callback runs later. Some run immediately, in order. Sorting an array, for example, calls your comparison function right away. Others run after a delay or once a task finishes, which is the asynchronous case.
| Type |
When it runs |
Common example |
| Synchronous |
Immediately, in sequence |
Array sort, map, filter callbacks |
| Asynchronous |
Later, after a task completes |
Timers, network requests, file reads |
The asynchronous case is the powerful one. It lets a program start a slow job, like fetching data, and keep doing other work. When the data arrives, the callback runs with the result. That is how a single-threaded language stays responsive.
Callback hell and what replaced it
The downside shows up when one async step depends on the next, which depends on the next. You end up nesting callbacks inside callbacks, drifting further right across the screen. Developers call this callback hell, and it is genuinely hard to read and debug.
Modern code mostly avoids it with promises and async-await, which let you write asynchronous steps in a flat, top-to-bottom style. They did not delete callbacks; they wrap the same idea in a cleaner shape. Understanding callbacks first makes promises far easier to grasp, so see what a promise in JavaScript is once this lands.
What to skip
- Deep nesting. If callbacks pile up more than two or three levels, switch to promises or async-await.
- Calling instead of passing. Pass
fn, not fn(), unless you specifically want the result.
- Forgetting error handling. Async callbacks fail; always handle the error path, not just success.
- Overusing callbacks for simple sync logic. Plain function calls are clearer when nothing is asynchronous.
FAQ
What is a callback in simple terms?
A function you give to another function so that other code can run it later, at the moment it is ready.
Why are callbacks used for asynchronous tasks?
They let a program start a slow job and keep working, then run your code once the job finishes, instead of freezing while it waits.
Are callbacks outdated?
No. Promises and async-await are built on top of callbacks and are nicer for chained steps, but callbacks are still everywhere and still essential to understand.
What is callback hell?
The tangled, deeply nested code you get when many async callbacks depend on each other. Promises and async-await are the usual fix.
Where to go next
What is a parameter, What is a promise in JavaScript, and What is async await.