👉 A function that is passed as an argument to another function.
Note :
- The function is then executed sometimes later inside the body.
- Everything in JavaScript are object, and we can pass objects inside an function as parameter .
Why do we need callback
♦️ JavaScript runs code sequentially in top-down order. However, there are some cases that code runs (or must run) after something else happens and also not sequentially. This is called asynchronous programming.
♦️ Callbacks helps you control the order of execution. It makes sure that a function is not going to run before a task is completed but will run right after the task has completed. It helps us develop asynchronous JavaScript code and keeps us safe from problems and errors.
How to create an callback function
- Creating callback as function expression
function greetUser(name, callback) {
console.log("Hi " + name);
callback();
}
function sayBye() {
console.log("Bye!");
}
greetUser("Rahul", sayBye);
Here the message function is being called after 3 second meaning we are holding the function for some time and then calling , hence this is one of the advantage of callback functions
- Creating as Anonymous Function (with
setTimeOut
)
setTimeout(function() {
console.log("This message is shown after 3 seconds");
}, 3000);
- Callback as an Arrow Function
setTimeout(() => {
console.log("This message is shown after 3 seconds");
}, 3000);
All 3 will do the same job
Callback helps in asynchronous programming
👉 Callback help in executing a code only after a following response and so on.
Here the function will wait for 3 seconds to print , hence we are able to control the execution according to our need.
const printMeAfter3sec = () => {
console.log("hello");
}
setTimeout(printMeAfter3sec, 3000);
Let’s understand how callback works:
console.log("Loading...")
function fetchDataCallback(callback) {
console.log("Fetching data...")
setTimeout(() => {
const data = { id: 1, name: "John Doe" } // Simulating fetched data
callback(data) // Call the callback with the data after fetching
}, 2000) // Simulate a 2-second delay
}
// Using the function with a callback
fetchDataCallback((result) => {
console.log("Data fetched:", result)
})
console.log("End...")
Explanation
Diagram explaining how its work behind the scene

1. Global Execution Context
When JavaScript starts executing this code, it first creates a Global Execution Context. This includes all the variables, function declarations, and any code that needs to run at the top level.
- The function
fetchDataCallback
is defined and stored in memory. - The
fetchDataCallback
function isn’t executed yet, just defined.
2. Function Invocation (Synchronous Part)
fetchDataCallback((result) => {
console.log('Data fetched:', result);
});
- Now,
fetchDataCallback
is invoked with a callback function passed as an argument. - A new execution context is created for this function call, where the
callback
parameter is assigned the anonymous function(result) => { console.log('Data fetched:', result); }
.
3. Execution of console.log('Fetching data...')
Inside fetchDataCallback
, the first statement is:
console.log('Fetching data...');
- This is a synchronous operation, so it immediately logs "Fetching data…" to the console.
4. Encountering setTimeout
Next, we encounter this code:
setTimeout(() => {
const data = { id: 1, name: 'John Doe' };
callback(data);
}, 2000);
setTimeout
is a Web API provided by the browser (or Node.js in server environments).- It takes two arguments:
- A callback function (which we want to execute after the delay).
- A delay (2000 milliseconds or 2 seconds).
- When JavaScript encounters
setTimeout
, it offloads this task to the Web API environment (outside of the JavaScript engine).
Important: The Web API (in this case, setTimeout
) handles the timer in the background while the main JavaScript thread continues executing other code.
5. Finishing the Synchronous Code
After setTimeout
is sent to the Web API, the synchronous code inside fetchDataCallback
finishes execution. At this point, the JavaScript call stack is empty because there are no more statements left to execute.
6. Timer and Web APIs (Asynchronous Part)
- While the main thread is free, the Web API timer runs in the background for 2 seconds (2000ms).
- After 2 seconds, the timer expires, and the callback function inside
setTimeout
(() => { const data = { id: 1, name: 'John Doe' }; callback(data); }
) is sent to the task queue.
7. Event Loop
- The event loop continuously monitors the call stack and the task queue.
- Once the call stack is empty (which it is after the synchronous code finishes), the event loop pushes tasks from the task queue to the call stack.
8. Callback Execution (Async Code)
After 2 seconds, the event loop pushes the callback function inside setTimeout
to the call stack:
() => {
const data = { id: 1, name: 'John Doe' };
callback(data);
}
- Inside this function:
- The
data
object is created:{ id: 1, name: 'John Doe' }
. - The
callback
function is invoked with thisdata
object as an argument.
- The
9. Executing the Passed Callback
Now, the callback (result) => { console.log('Data fetched:', result); }
is called with the data
as result
.
- A new execution context is created for this callback.
- The statement
console.log('Data fetched:', result)
is executed. - It logs the final result:
Data fetched: { id: 1, name: 'John Doe' }
to the console.
Visual Timeline:
- Start: Function
fetchDataCallback
is called → logs "Fetching data…" - setTimeout: The
setTimeout
function is offloaded to Web APIs → 2-second timer starts. - Synchronous Code Ends: Main thread becomes idle → waits for async tasks.
- After 2 Seconds: Timer expires → callback is pushed to task queue → event loop moves it to the call stack.
- Callback Executes: Logs the final data.
Summary of Web APIs Role:
- The Web API (
setTimeout
) is responsible for the timer. - The event loop ensures that after the timer expires, the callback is executed asynchronously once the call stack is clear.
- Callbacks allow us to handle asynchronous operations like these, where some operations (like waiting for data) are handled by the browser or Node.js outside of the JavaScript execution engine.
Let’s understand this with one more problem statement –
In the below code . the output of message is coming as “undefined” .
console.log("hello");
function asyncMe(name) {
setTimeout(function () {
return `${name}`;
}, 1000);
}
const message = asyncMe("himanshu");
console.log(message);
console.log("bye");
Reason → The message o/p will come undefined , because asyncMe() is an asynchronous function meaning it will execute at last.
To fix the above code, we can use callback
Just define a functions inside a parameter (i.e callback) , and pass it inside setTimeout() , hence solved
console.log("hello");
function asyncMe(name, callback) {
setTimeout(function () {
const message = `print to ${name}`;
callback(message); // Call the callback function with the message
}, 1000);
}
asyncMe("himanshu", function(message) {
console.log(message); // This will be executed after the timeout
});
console.log("bye");
In coming lectures, we will see the other ways of fixing the above code using promise and async await
Question : Fix this with callback
function getUserData(id) {
setTimeout(() => {
if (!id) {
throw new Error("User ID is required!");
}
return { id, name: "John Doe" }; // This return won't work properly in async code
}, 1000);
}
const user = getUserData(1);
console.log(user); // This will be undefined
Solution :
function getUserData(id, callback) {
setTimeout(() => {
if (!id) {
return callback(new Error("User ID is required!"), null);
}
const user = { id, name: "John Doe" };
callback(null, user);
}, 1000);
}
// Using the function with a callback
getUserData(1, (error, user) => {
if (error) {
console.error("Error:", error.message);
} else {
console.log("User Data:", user);
}
});
// Testing the error case
getUserData(null, (error, user) => {
if (error) {
console.error("Error:", error.message);
} else {
console.log("User Data:", user);
}
});
Other uses of callback
Callback helps in calling an event
//html
<button id="callback-btn">Click here</button>
//java script
document.queryselector("#callback-btn")
.addEventListener("click", function() {
console.log("User has clicked on the button!");
});
The first one is its type, “click”, and the second parameter is a callback function, which logs the message when the button is clicked.
Callback function helps in creating a polyfill
For example, let create a polyfill for for each
let arr= [2,4,5,3,5 ];
const myForEach = (arr,cb) => {
for(let i=0;i<arr.length;i++){
const element = arr[i];
cb(element)
}
}
myForEach(arr , (name)=> console.log(name));
🏠 Home work – Try creating polyfill for map, filter and reduce. Link