In JavaScript, callbacks are used for handling operations like reading files and making API requests. When there is excessive nesting of the functions it leads to a problem known as the callback hell. Due to this, it becomes difficult to read the code, debug, and maintain. But when we implement the promises and async/await it helps in improving the code.
What are Callbacks in JavaScript?
In JavaScript, callbacks are functions that are passed as arguments from one function to another and are executed after the completion of a certain task. They are commonly used in asynchronous operations, such as reading files, making HTTP requests, or handling user input.
A function can accept another function as a parameter.
Callbacks allow one function to call another at a later time.
A callback function can execute after another function has finished.
How Do Callbacks Work in JavaScript?
JavaScript executes code line by line (synchronously), but sometimes we need to delay execution or wait for a task to complete before running the next function. Callbacks help achieve this by passing a function that is executed later.
Example of a Callback
function greet(name, callback) {
console.log(`Hello, ${name}!`);
callback(); // calling the callback function
}
function afterGreet() {
console.log('Greeting is complete!');
}
greet('Anjali', afterGreet);
Output
Hello, Anjali!
Greeting is complete!
Problem with Callbacks
Callback Hell (Pyramid of Doom)
When multiple asynchronous operations depend on each other, callbacks get deeply nested, making the code hard to read and maintain.
getUser(userId, (user) => {
getOrders(user, (orders) => {
processOrders(orders, (processed) => {
sendEmail(processed, (confirmation) => {
console.log("Order Processed:", confirmation);
});
});
});
});
What is Callback Hell?
Callback Hell in JavaScript can be defined as the situation where we have nested callbacks(functions passed as arguments to other functions) which makes the code difficult to read and debug. The term "callback hell" describes the deep nesting of functions that can result in poor code readability and difficulty in debugging, especially when handling multiple asynchronous operations.
function task1(callback) {
setTimeout(() => {
console.log("Task One completed");
callback();
},);
}
function task2(callback) {
setTimeout(() => {
console.log("Task Two completed");
callback();
},);
}
task1(function () {
task2(function () {
console.log("Both tasks completed");
});
});
Output
Task One completed
Task Two completed
Both tasks completed
In this example
The two tasks (task1 and task2) are executed one after the other.
Each task has a callback that triggers the next task, causing the callback to be nested inside the other, leading to Callback Hell.
Solution to Callback Hell
Promises
Promises can help in avoiding the callback hell by providing the structured way to handle the asynchronous operations using the .then() method. Due to which the code becomes more readable by avoiding the deeply neseted callbacks.
For more details read this article - JavaScript Promises
Async/await
Async/await can help in avoiding the callback hell by writing the asynchronous code that looks like the synchronous code due to which the code becomes more cleaner and readable and reduces the complexity.