Callbacks in JavaScript: Why They Exist
What is a callback function?
Let's understand how you operate in the real world when you order food at a restaurant. You don't stand frozen on the counter staring at the kitchen until your food arrives. You go sit down, you may talk with somebody, or check your phone, and when the food is ready, the waiter calls you back to pick it up or brings it to you.
That is exactly what a callback function is
You hand off the task to someone else, along with instructions for what to do when it's done. You don't wait for its completion; you continue doing other things, and when the task finishes, your instructions get executed.
Now let's see the technical theory
A callback function is a function that is passed as an argument to another function. which is the call inside the outer function to complete some kind of task. It says when you are done with your job, run this.
Here is an example code of a callback function
function doSomething(callback) {
// do something
callback();
}
doSomething(function () {
// do something else
});
The problem it solves
TO understand the problem why the callback exits, you need to understand what would happen without them.
The Blocking Problem: Imagine JavaScript worked synchronously for everything, including slow operations:
let data = fetch('https://api.example.com/data'); // this task takes 2 seconds to complete
console.log(data); // now continue
During those 2 seconds, nothing else runs. No user interaction, no animations, no other code. The single thread of JS is stuck, waiting.
This is called blocking. For language that runs on a browser and is responsible for keeping UI alive and responsive, blocking is catastrophic.
The core Tention: JavaScript is single threaded. Slow operations exist (network, disk I/O, timers). You cannot avoid slow operations. You cannot add more threads (not easily, and not as a default model). So you need a way to schedule future work without stopping the present work.
Callbacks solve this by separating 2 things:
When you start your task.
What happens when the task finishes?
The core concepts
Before we go deeper, let's understand three core concepts.
Functions are first class values in JS: This is a foundational idea in JavaScript that makes callbacks possible at all. In JavaScript, functions is not just something you can call. It is a value like a number, a string, boolean. You can:
Assign it to a variable.
Store in object
Pass it as an argument to another function
Return it from the function.
This is what "first-class functions" means. In many older languages, you couldn't pass a function as an argument the way you can in JavaScript. The fact that JavaScript treats functions as values is what makes the entire callback pattern possible.
Here is an example
// greet is function
function greet() {
console.log('Hello');
}
// greet is just a value here, not being called
setTimeout(greet, 1000);
//getProducts is a basic function that call api and returns data
. Function getProducts() {
const data = fetch('https://api.example.com/data');
const res = data.json();
return res;
}
// here we pass the function as an argument
const products = getProducts();
Synchronous vs Asynchronous execution
- Synchronous means: execute now, in order to wait for each step to complete before moving to the next.
/Asynchronous means: start this task, don't wait for it, continue running other code, and handle the result later.
Higher order functions
A function that accepts another function as a parameter, or returns a function, is called a higher order function. setTimeout, addEventListener, and Array.forEach are all higher order functions. They receive your callback and decide when and how to call it.
Why Callbacks Are Used in Asynchronous Programming
Now you know what a callback is and what blocking is, let's connect both points.
JavaScript runs on a single thread, that mean excuting one thing at a time. There is no parallel execution happening in your JS code. One line finishes, then the next starts
But there are some tasks that take time to execute:
Fetching data from the server
Reading file from disk
A timer or seconds
If JavaScript waited for each of these, the entire program would freeze. So JavaScript uses a system called the Event Loop.
Here is digram
When you call setTimeout or fetch, JavaScript doesn't run those itself. It hands them off to the browser's Web APIs. The browser handles the slow work. Your JS call stack is free to keep running.
When the slow task finishes, the browser puts your callback into the Callback Queue. The Event Loop watches the call stack. When the stack is empty, it picks the next callback from the queue and runs it.
Let's see with an example:
console.log('Start');
setTimeout(function () {
console.log('Inside timeout');
}, 1000);
console.log('End');
//output
/*
Start
End
Inside timeout
*/
-1 Start runs immediately -2 setTimeout hands the callback to the browser and moves on it does not wait to complete -3 End runs immediately -4 1 second later, browser puts the callback in the queue -5 Event Loop sees the stack is empty, runs the callback -6 "Inside timeout" prints
Callback Usage in Common Scenarios
Callbacks aren't just theory, you use them often without even noticing it. Here are common places
1 Timers
setTimeout(function () {
console.log('This runs after 2 seconds');
}, 2000);
You are telling JavaScript to wait 2 seconds, then call this function. This function is your callback. You didn't call it yourself, setTimeout calls it for you. when the time is right.
2 Event Listeners
const button = document.querySelector("button");
button.addEventListener("click", function(event) {
console.log("Button was clicked!");
});
Here, you don't know when the user will click the button. You just register a callback and say when this click happens, run this. The browser calls your function when the event happens.
This is one of the most important uses of callbacks. Events are inherently unpredictable; they happen in response to user actions. Callbacks are the perfect fit.
3. Array Methods (Synchronous Callbacks)
Not all callbacks are async. Array methods like forEach, map, and filter use callbacks too, but they call them immediately, one by one, in order.
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(num) {
return num * 2;
});
console.log(doubled); // [2, 4, 6, 8, 10]
You're passing a function to map, and map calls it for each element. This is still a callback; you're still passing a function as an argument. The difference is that it's synchronous. Understanding this distinction matters: callbacks are a pattern, not an async mechanism by themselves.
4. Fetching Data (The Classic Async Use Case)
Before promises existed, all network requests were handled with callbacks.
function getUser(userId, callback) {
setTimeout(function() {
// Simulating a network request
const user = { id: userId, name: "John" };
callback(user);
}, 1000);
}
getUser(1, function(user) {
console.log("Got user:", user.name);
});
You call getUser, pass your callback, and your callback receives the result when it's ready. This pattern was the foundation of async JavaScript for years.
The Problem of Callback Nesting
Callbacks work well for a single async operation. But real applications have dependent async operations where you need the result of one to start the next. For example:
Get a user by ID Use that user's ID to get their orders Use the first order's ID to get the order details
With callbacks, each step is nested inside the previous one:
getUser(1, function(user) {
getOrders(user.id, function(orders) {
getOrderDetails(orders[0].id, function(details) {
console.log("Finally got details:", details);
});
});
});
This is called callback hell or the pyramid of doom (look at the shape the indentation creates). Now imagine adding error handling at each level, or adding two more dependent steps. It becomes nearly unreadable.
This is exactly why Promises were introduced in ES6, and why async/await arrived in ES2017. They don't replace the callback mechanism underneath; they give you cleaner syntax to express the same sequential async logic without the pyramid.
Hope you learn something from this blog