Callbacks in JavaScript: Why They Exist

I build clean and simple web experiences and learn something new every day.
If you’ve been learning JavaScript, you’ve definitely seen this word callback
And at first it feels like:
Okay… it’s a function… but why is it passed inside another function???
And then when async comes in, it gets even more confusing. So in this blog, I’m going to break it down from zero to clear understanding no confusion, no heavy jargon. Just simple logic.
Functions in JavaScript Are Not “Special”
Before we even talk about callbacks, you need to understand one thing. In JavaScript, functions are just values. Yes, just like: numbers, strings, arrays.
Functions can also be stored in variables, passed as arguments, returned from other functions
Let me show you.
function sayHello() {
console.log("Hello");
}
const greet = sayHello;
greet(); // Hello
We didn’t call sayHello() directly. We stored it in a variable and called it later. That’s because functions are treated like values.
Now Let’s Build the Idea of Callback
Now imagine this:
function greet(name) {
console.log("Hello " + name);
}
function processUserInput(callback) {
const name = "Nausheen";
callback(name);
}
processUserInput(greet);
Look carefully. We passed greet as an argument. And then inside another function, it was called. This is a callback.
So What Exactly Is a Callback?
Let me say it in the simplest way: A callback is a function passed into another function to be called later. That’s it, nothing complicated.
Think About It Like This
Imagine you tell your friend:
Call me when you reach home.
You didn’t call them. You gave them a function (instruction) to execute later. That’s a callback.
Main Function => receives another function => executes it later
Why Do Callbacks Even Exist?
Now comes the real question. Why do we even need callbacks?
The answer is simple: Because of asynchronous JavaScript.
Let’s Understand With a Real Problem
Imagine this:
console.log("Start");
setTimeout(() => {
console.log("Data received");
}, 2000);
console.log("End");
Output:
Start
End
Data received
Now think. The data comes after 2 seconds. So how do we run code after the data is ready? We use a callback.
Callback Solves This Problem
function fetchData(callback) {
setTimeout(() => {
const data = "User Data";
callback(data);
}, 2000);
}
function displayData(data) {
console.log(data);
}
fetchData(displayData);
Here’s what’s happening: fetchData starts async work, after 2 seconds, it gets data, then it calls the callback. This ensures the code runs at the right time
This Is the Core Idea
Callbacks allow you to say:
Run this function AFTER something is done.
Common Places You Already Use Callbacks
Even if you didn’t realize, you’re already using callbacks.
1. setTimeout
setTimeout(() => {
console.log("Runs later");
}, 1000);
That arrow function is a callback.
2. Event Listeners
button.addEventListener("click", () => {
console.log("Clicked!");
});
That function runs only when the event happens.
Callback.
3. Array Methods
const numbers = [1, 2, 3];
numbers.forEach((num) => {
console.log(num);
});
The function inside forEach is also a callback.
Now Comes the Problem…
Callbacks are powerful. But they can get messy. Let’s see how.
Callback Nesting (Callback Hell)
Imagine this:
setTimeout(() => {
console.log("Step 1");
setTimeout(() => {
console.log("Step 2");
setTimeout(() => {
console.log("Step 3");
}, 1000);
}, 1000);
}, 1000);
Now look at this structure.
Step 1
↓
Step 2
↓
Step 3
The code keeps going deeper and deeper. This is called: Callback Hell
Why Callback Hell Is a Problem
Hard to read
Hard to debug
Hard to maintain
You lose track of logic.
Visualizing Nested Callbacks
Task 1
└── Task 2
└── Task 3
└── Task 4
Looks messy, right? That’s the problem.
So What Did Developers Do?
Callbacks solved async problems… But created readability problems.So developers introduced Promises, Async/Await. But callbacks are still important. Because everything async in JS is built on top of callbacks.
One Simple Way to Think About Callbacks
Whenever you see a callback, just think: “This function will run later.”
That’s it.
Small Practice Example
Try this:
function greetUser(name, callback) {
console.log("Hi " + name);
callback();
}
function sayBye() {
console.log("Bye!");
}
greetUser("Nausheen", sayBye);
Output:
Hi Nausheen
Bye!
Final Thoughts
Callbacks might feel confusing at first… But once you understand this - Functions can be passed like values, they can be executed later, they help handle async tasks. Everything becomes clear.
Key Takeaway
Callback = function passed into another function
Used to run code later
Very common in async JavaScript
Can become messy when nested
And honestly…
Once callbacks click, understanding Promises and Async/Await becomes 10x easier.

