The callback is a very helpful and important feature of the Javascript language. As you know, Javascript is a single-threaded language which means, it doesn’t stop executing a program until it is completed but some functions might need time to execute the code. So we needed to execute some functions asynchronously, where callback plays a major role! But one of the major drawbacks of the callback function is callback hell.
In this blog, we will analyze and understand why callback is needed, what really means by callback hell, and how to avoid it!
Need of Callback functions
Javascript is a single-threaded language, which means it executes programs, line by line, in a sequence (synchronous) manner. So if a function takes time to execute, it blocks the execution, and programs have to wait until the current line doesn’t execute, and then only it moves forward.
So we needed, Callback that could perform asynchronously and don’t stop the program execution.
Example
Let us execute down below some pieces of code!
console.log(‘First’)
console.log(‘Second’)
console.log(‘Third’)
// First
// Second
// Third
But what if we want to print the second statement after a few mins, in order to do that we will take the help of callback and setTimeout function to do that!
console.log(‘First’)
setTimeout(() =>{
console.log(‘Second’)
}, 2000)
console.log(‘Third’)
// First
// Third
// second
Also read, How to debug JavaScript in browser?
What is Callback Hell?
A callback Hell is a phenomenon where the numbers of callbacks are nested together within a function usually forming a horizontal Pyramid type structure, also known as “Pyramid Doom”.
asyncFunction(function(){
asyncFunction(function(){
asyncFunction(function(){
asyncFunction(function(){
asyncFunction(function(){
....
});
});
});
});
});
One of the main disadvantages of it is that it makes code highly difficult to maintain and update. This type of problem you will generally encounter in node js but you can also avoid callback hell in js by using async/await.
To understand this phenomenon let us see an example, assume you need to get the total number of post number written by the author in march month, but before that, you first need to get all author data, then from all author data, you need to find that specific author, then from that specific author, you need to find the month, and then from then we get the total number of post.
Example
const getAllAuthor = (allAuthor) => {
// Some line of code....
console.log("All Author data ->", allauthor);
getAuthor(author_id, (allauthor) => {
// Some line of code....
console.log("Specific Author data ->", author);
getMonthData(month, (author) => {
// Some line of code....
console.log("Month data ->", monthData);
getTotalNumberPost((monthData) => {
// Some line of code....
console.log("Total Number of Post ->", totalNumberofPost);
});
});
});
};
In the above example, as you in the above code, in order to get the total number of past, we had to go through nested callback functions. As requirement gets more complex, this type of code becomes unreadable and unmaintainable in no time.
Also read, What is JavaScript Event Loop?
How to avoid Callback Hell?
It is not easy to avoid Callback hell, but you can follow some best practices which can help in reducing it. Here are three rules you can follow in order to avoid callback hell:-
Descriptive Function Name
Having the correct naming convention may not seem like a big deal, but using descriptive function names makes code easier to read, Number of benefits from it are:-
- Makes code easier to read and understand
- If any error happens, it is easy to trace back due to the descriptive function name, instead of using an anonymous function.
- Allow easy editing or changing function due to its name reference.
Also read, How to Use Swift for Web Development
Modularize
Try to break your junk into different modules and then assemble them into a bigger function so that it becomes easy to update and maintain your code.
Normal Way
const main = () => {
console.log("this is main function");
function first() {
console.log("this is first function");
function second() {
console.log("this is second function");
function third() {
console.log("this is third function");
function fourth() {
console.log("this is fourth function");
}
}
}
}
};
Modular Way
function fourth() {
console.log("this is fourth function");
}
function third(fourth) {
console.log("this is third function");
fourth();
}
function second(third) {
console.log("this is second function");
third(fourth);
}
function first(second) {
console.log("this is first function");
second(third);
}
const main = (first) => {
console.log("this is main function");
first(second);
};
Error Handling
One of the biggest disadvantages of the callback is every callback function is dependent on its parents function for its execution, if any error happens at any level then the successor function gets directly affected by it so in order to avoid it, at every level of function you need to maintain error handling.
For best practice, always have an error handling condition as your first argument of the callback function, so that if an error happens, you can get noticed there and your bug does not travel to the successor function.
Also read, What is a Callback Functions in JavaScript
Final Words
If you enjoyed this article, please consider sharing it with your friends and colleagues. Additionally, feel free to explore our other tutorial articles covering a variety of topics such as Data Structure algorithms with JavaScript, React, Java, and more.