Callbacks and Closures in JavaScript: A Clear Comparison


In JavaScript, callback functions and closures are two essential concepts that involve functions within functions. While both allow for greater flexibility and control over code execution, they serve different purposes:
Callbacks are functions passed as arguments to other functions and executed later, often used for asynchronous operations like API calls, event handling, and sequencing tasks.
Closures are functions that remember the scope in which they were created, allowing them to maintain private variables and state even after their parent function has executed.
Understanding these concepts is crucial for writing efficient, reusable, and modular JavaScript code. Letβs understand one by one.
πΉ Callback Functions
A callback function is a function that is passed as an argument to another function and is executed later, usually after some asynchronous operation is completed.
β Purpose:
Used to handle asynchronous operations (e.g.,
setTimeout
, event listeners, API calls).Helps in executing functions in sequence (important in async programming).
π₯ Example :
function fetchData(callback) {
setTimeout(() => {
console.log("Data fetched");
callback(); // Executing the callback function
}, 2000);
}
function processData() {
console.log("Processing data...");
}
fetchData(processData); // Passing processData as a callback
π Here: processData
is passed as a callback to fetchData
, ensuring it runs after the data is fetched.
πΉ More Examples of Callback Functions
π 1. Using Callbacks in an API Request (Simulated)
function fetchUserData(userId, callback) {
console.log(`Fetching data for user ${userId}...`);
setTimeout(() => {
const user = { id: userId, name: "Joe" };
callback(user); // Calling the callback after fetching data
}, 2000);
}
function displayUser(user) {
console.log(`User Name: ${user.name}`);
}
fetchUserData(101, displayUser);
π Here:
fetchUserData
simulates fetching user data from an API.displayUser
is passed as a callback and runs after fetching data.
π 2. Callbacks in Event Handling
document.getElementById("myButton").addEventListener("click", function() {
console.log("Button was clicked!");
});
π Here:
- The function inside
addEventListener
is a callback function, executed when the button is clicked.
π 3. Callbacks in Array Methods
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(num) {
return num * 2;
});
console.log(doubled); // [2, 4, 6, 8, 10]
π Here:
map
takes a callback function to process each element in the array.
π 4. Simulating an API Call with Callbacks
function fetchData(url, successCallback, errorCallback) {
console.log(`Fetching data from ${url}...`);
setTimeout(() => {
let success = Math.random() > 0.3; // Simulating 70% success rate
if (success) {
successCallback({ data: "Sample API Data" });
} else {
errorCallback("Error: Unable to fetch data!");
}
}, 2000);
}
fetchData(
"https://api.example.com/data",
(response) => console.log("Data received:", response.data),
(error) => console.error(error)
);
π Here:
If the API call is successful,
successCallback
is executed.Otherwise,
errorCallback
handles the failure.
π 5. Using Callbacks to Control Execution Order
function step1(callback) {
console.log("Step 1 completed.");
callback();
}
function step2(callback) {
console.log("Step 2 completed.");
callback();
}
function step3() {
console.log("Step 3 completed.");
}
step1(() => step2(() => step3()));
π Here:
- The functions execute in order because each function calls the next one.
π 6. Using Callbacks in Sorting
const students = [
{ name: "Alice", age: 22 },
{ name: "Bob", age: 25 },
{ name: "Charlie", age: 20 }
];
students.sort((a, b) => a.age - b.age); // Sort by age (ascending)
console.log(students);
π Here:
sort
takes a callback to compare student ages.
π΅ 7. Using Callbacks for Audio Player
function playSong(song, callback) {
console.log(`Playing ${song}...`);
setTimeout(callback, 3000); // Simulate song duration
}
function nextSong() {
console.log("Playing next song...");
}
playSong("Song 1", nextSong);
π Here:
nextSong
runs after the current song finishes.
πΉ Closures
A closure is a function that "remembers" the scope in which it was created, even after that scope has finished executing.
β Purpose:
Preserves variable state even after the outer function has returned.
Useful for data encapsulation (e.g., private variables).
Avoids global scope pollution.
π₯ Example:
function createCounter() {
let count = 0; // Private variable
return function() {
count++;
console.log(`Count: ${count}`);
};
}
const counter = createCounter(); // Returns inner function
counter(); // Count: 1
counter(); // Count: 2
counter(); // Count: 3
π Here:
The inner function "remembers"
count
even aftercreateCounter
is finished.This is because the function closes over (
captures
) thecount
variable.
πΉ More Examples of Closures
π― 1. Creating a Private Counter
function createCounter() {
let count = 0;
return function() {
count++; // "count" is remembered
console.log(`Current count: ${count}`);
};
}
const counter = createCounter();
counter(); // Current count: 1
counter(); // Current count: 2
counter(); // Current count: 3
π Here:
- The inner function retains access to
count
even thoughcreateCounter
has already executed.
π¦ 2. Closure for Creating a Bank Account
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit: function(amount) {
balance += amount;
console.log(`Deposited: $${amount}, New Balance: $${balance}`);
},
withdraw: function(amount) {
if (amount > balance) {
console.log("Insufficient funds!");
} else {
balance -= amount;
console.log(`Withdrew: $${amount}, New Balance: $${balance}`);
}
},
checkBalance: function() {
console.log(`Balance: $${balance}`);
}
};
}
const myAccount = createBankAccount(1000);
myAccount.deposit(500); // Deposited: $500, New Balance: $1500
myAccount.withdraw(200); // Withdrew: $200, New Balance: $1300
myAccount.checkBalance(); // Balance: $1300
π Here:
balance
is private insidecreateBankAccount
, but accessible through the returned functions.
π₯ 3. Function Factory (Custom Multipliers)
function createMultiplier(multiplier) {
return function(num) {
return num * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
π Here:
- The inner function "remembers"
multiplier
, even aftercreateMultiplier
has finished executing.
π¦ 4. Creating a Secure Wallet
function createWallet(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) {
balance += amount;
console.log(`Deposited $${amount}. New Balance: $${balance}`);
},
withdraw(amount) {
if (amount > balance) {
console.log("Insufficient funds!");
} else {
balance -= amount;
console.log(`Withdrew $${amount}. New Balance: $${balance}`);
}
},
getBalance() {
return balance;
}
};
}
const myWallet = createWallet(500);
myWallet.deposit(200); // Deposited $200. New Balance: $700
myWallet.withdraw(100); // Withdrew $100. New Balance: $600
console.log(myWallet.getBalance()); // 600
π Here:
balance
is private and can only be modified viadeposit
andwithdraw
.
π’ 5. Custom Event Logger
function createLogger(module) {
return function(message) {
console.log(`[${module}] ${message}`);
};
}
const authLogger = createLogger("Auth");
const dbLogger = createLogger("Database");
authLogger("User logged in."); // [Auth] User logged in.
dbLogger("Connection established."); // [Database] Connection established.
π Here:
createLogger
returns a function that "remembers" the module name.
π΅οΈ 6. Private Counter
function counter() {
let count = 0;
return {
increment() {
count++;
console.log(`Count: ${count}`);
},
decrement() {
count--;
console.log(`Count: ${count}`);
}
};
}
const myCounter = counter();
myCounter.increment(); // Count: 1
myCounter.increment(); // Count: 2
myCounter.decrement(); // Count: 1
π Here:
count
remains private, preventing direct modification.
π₯ 7. Function that Returns Different Greetings
function greeting(language) {
return function(name) {
if (language === "en") return `Hello, ${name}!`;
if (language === "es") return `Hola, ${name}!`;
if (language === "fr") return `Bonjour, ${name}!`;
return `Hi, ${name}!`;
};
}
const englishGreet = greeting("en");
const spanishGreet = greeting("es");
console.log(englishGreet("Joe")); // Hello, Joe!
console.log(spanishGreet("Carlos")); // Hola, Carlos!
π Here:
greeting("en")
creates a closure that remembers"en"
.
π 8. Delayed Execution with Closures
function delayedMessage(message, delay) {
return function() {
setTimeout(() => {
console.log(message);
}, delay);
};
}
const greetLater = delayedMessage("Hello, after 3 seconds!", 3000);
greetLater();
π Here:
- The inner function remembers
message
anddelay
even afterdelayedMessage
executes.
π° 9. Lottery Number Generator
function lottery() {
let secretNumber = Math.floor(Math.random() * 100) + 1;
return function(guess) {
if (guess === secretNumber) {
console.log("Congratulations! You guessed the right number.");
} else {
console.log("Try again!");
}
};
}
const play = lottery();
play(50); // "Try again!"
play(75); // "Try again!"
play(30); // "Congratulations!" (if correct)
π Here:
secretNumber
remains private, ensuring fair play.
π₯ Key Differences
Feature | Callback Functions | Closures |
Definition | Function passed as an argument and executed later | Function that remembers the scope it was created in |
Purpose | Handles async operations or executes functions in sequence | Retains variables even after the outer function has finished |
Execution | Called later inside another function | Executes immediately when invoked |
Example Use Cases | setTimeout , API calls, event listeners | Data encapsulation, state management |
π― Final Thoughts
Use Callbacks when handling async operations, executing functions after an event, or processing data sequentially.
Use Closures when you need private variables, persistent state, or function customization.
Conclusion :
Both callback functions and closures enable more dynamic, flexible, and functional programming in JavaScript. Callbacks help manage async operations and execution flow, while closures allow functions to retain state and create encapsulated logic.
By mastering these concepts, you can write more efficient, maintainable, and scalable JavaScript applications, whether youβre working with event-driven programming, API calls, or modular design patterns. π
Subscribe to my newsletter
Read articles from Indrajeet directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Indrajeet
Indrajeet
Hello there! My name is Indrajeet, and I am a skilled web developer passionate about creating innovative, user-friendly, and dynamic web applications. πMy Expertise advanced proficiency in angular front-End Development. Back-End Development Leveraging the power of .NET, I build secure, robust, and scalable server-side architectures.