Callbacks in JavaScript
Why They Exist?

What is a Callback Function?
A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.
Example:
function sayHello() {
console.log('Hello World!');
}
function executeCallbackFunction(callback) {
callback();
}
executeCallbackFunction(sayHello);
There are two ways in which the callback may be called: synchronous and asynchronous. Synchronous callbacks are called immediately after the invocation of the outer function, with no intervening asynchronous tasks, while asynchronous callbacks are called at some point later, after an asynchronous operation has completed.
// Synchronous
function doSomething(callback) {
callback();
}
// Asynchronous
function doSomething(callback) {
setTimeout(callback, 0);
}
Why Callbacks are used in Asynchronous Programming?
JavaScript is single-threaded, meaning it executes one task at a time. But real-world tasks take time. These tasks are, for example:
Fetching data from an API
Reading files
Waiting for user input
Callbacks help us handle these delays without blocking execution.
Example with setTimeout:
console.log('Start');
setTimeout(() => {
console.log('This runs later');
}, 2000);
console.log('End');
// Output:
// Start
// End
// This runs later
The callback inside setTimeout runs after 2 seconds, without stopping the rest of the code.
Passing functions as Arguments
This is where callbacks become powerful.
Example:
function calculate(a, b, operation) {
return operation(a, b);
}
function add(x, y) {
return x + y;
}
function multiply(x, y) {
return x * y;
}
console.log(calculate(2, 3, add)); // 5
console.log(calculate(2, 3, multiply)); // 6
The behavior of calculate changes based on the callback passed.
Callback usage in common scenarios
- Event Handling:
button.addEventListener('click', () => {
console.log('Button clicked!');
});
- Array Methods:
const numbers = [1, 2, 3];
const doubled = numbers.map((num) => num * 2);
console.log(doubled); // [2, 4, 6]
- API Calls (Before Promises):
function fetchData(callback) {
setTimeout(() => {
callback('Data received');
}, 1000);
}
fetchData((data) => {
console.log(data);
});
The Problem of Callback Nesting
When callbacks depend on other callbacks, code can become messy and hard to read. Example:
setTimeout(() => {
console.log('Step 1');
setTimeout(() => {
console.log('Step 2');
setTimeout(() => {
console.log('Step 3');
}, 1000);
}, 1000);
}, 1000);
This pyramid structure is known as callback hell.
Problems:
Hard to read
Hard to debug
Hard to maintain
Think of it like this:
You’re waiting for Task A
Then inside it, waiting for Task B
Then inside it, waiting for Task C
Each step is dependent on the previous one, creating deep nesting.
Conclusion
Callbacks are the backbone of asynchronous JavaScript. Even modern tools like Promises and async/await are built on top of this concept.
If you understand callbacks well, everything else becomes much easier.






