Scaler Topics LogoScaler Topics Logo
Search for Articles, Topics

What is Callback Hell in JavaScript?

Learn via video course
FREE
View all courses
JavaScript Course With Certification: Unlocking the Power of JavaScript
JavaScript Course With Certification: Unlocking the Power of JavaScript
by Mrinal Bhattacharya
1000
4.8
Start Learning
Start Learning View all courses
JavaScript Course With Certification: Unlocking the Power of JavaScript
JavaScript Course With Certification: Unlocking the Power of JavaScript
by Mrinal Bhattacharya
1000
4.8
Start Learning
Topics Covered

In software engineering, the term callback hell generally refers to an ineffective way of writing code asynchronously. It is an anti-pattern that is often called the “pyramid of doom”. Now, before we start learning about callback hell in JavaScript, there are certain terms that we need to understand, which will be covered next.

What is a Callback?

In JavaScript, everything (strings, arrays, functions) is considered an object. Hence, the concept of callbacks lets us pass functions as arguments to another function which will be executed later within the outer function.

Next, let’s discuss the importance of callbacks in JavaScript.

Assume we have a function called 'compute'. It takes in 3 parameters namely a string and two numbers. The string ‘action’ is compared through a series of if conditions and the desired operation is computed and returned as shown below.

This works fine, but what if we need to add a third or fourth operation? As the requirement increases, the number of if conditions inside the 'compute' function will also keep increasing which will lead to a messy code implementation as shown below.

Hence, we can rewrite the code in a better way as shown below.

Here, for each operation, we have defined its own function body independently. In this way, we can pass the functions as callbacks to the 'compute' function. Hence, if we want to add a new operation, say 'add', we first need to create a separate function for 'add' and then pass it as an argument in the compute function thus making it easier to understand.

The most common examples of callback functions in JavaScript are addEventListener, array functions (filter, map, reduce) etc.

Before we move ahead, let's understand the difference between pass-by-value and pass by reference in JavaScript.

JavaScript offers two sets of data types such as primitives and non-primitives. Primitve data types such as null, undefined, boolean, and string cannot be changed (immutable) while non-primitive data types include objects such as arrays and functions that can be changed (mutable).

Now, while passing by reference, we basically pass the address (reference) of the entity such as a function as an argument to the function being invoked. Hence, if the value of the argument inside the function is changed, then it correspondingly alters the original value found outside the function. On the other hand, passing an argument by value into the function will not change its value outside the function because it copies the value into two different locations in memory hence treating them as entirely separate entities. In JavaScript, all objects interact by reference.

In addEventListener, it listens for an event such as a click event, and accepts the second argument as a function that is to be executed once the click event is triggered. This function is passed as a reference without any parentheses. In the example shown below, the display function is passed as an argument into the addEventListener as the callback which later gets invoked when the click event happens.

Similarly in array functions, if we take the filter function as an example, it can take in another callback function as an argument. In the example shown below, largeNum is the callback function passed onto the fiter function on array arr.

Synchronous JavaScript

In general, synchronous programming has the following two features :

  1. A single task is executed at a time.
  2. The next set of following tasks will only be addressed once the current task gets finished. Thereby following a sequential code execution.

This style of programming gives rise to a “blocking code” situation as each task has to wait for the previous tasks to get done.

Now, when it comes to JavaScript, we often get confused about whether it’s synchronous or asynchronous.

In the above example for the filter function, the callback function gets executed inside the filter function synchronously. Hence, it is called a synchronous callback. The filter function has to wait for the largeNum callback function to finish execution. Hence, the callback function is also called blocking callbacks as it blocks the execution of the parent function in which it was invoked.

The prime definition of JavaScript claims that it is single-threaded, synchronous and blocking in nature. But interestingly, we can make it behave asynchronously based on our use cases.

Asynchronous JavaScript

The asynchronous style of programming focuses more on improving the performance of the application and callbacks can also be used in such situations. The asynchronous behavior of JavaScript can be explained using the setTimeout function as shown in the code below.

As shown above, the setTimeout function takes in a callback and time in milliseconds as arguments. The callback gets invoked after the time mentioned (here 1s). In short, the function has to wait for 1s for its execution. Now, to give more clarity observe the code below.

Here, the code written after the setTimeout function gets executed while the timer function waits for 1 second to pass. Hence, firstly, “One” gets printed, secondly “Two” gets printed and after 1 second the timer function invokes the callback function and prints “Timer displayed after 1 second”.

To understand the asynchronous behavior of JavaScript while using the setTimeout function we need to get familiar with the event loop. This might be beyond the scope of our article but we will give you a brief insight.

Our web browser consists of a JavaScript engine, web APIs, local storage, timers, etc. JavaScript engine contains a single call stack in which all the code is executed immediately as and when it is pushed without waiting.

Now, we need to understand that the setTimeout function is not a part of JavaScript but it is a part of the web browser as it is essentially a web API and the browser allows the JavaScript engine to access the setTimeout function with the help of the global window object.

Next, we need to understand that along with the call stack, the JavaScript engine also consists of a callback queue for dealing with asynchronous events easier.

Call Stack and a heap memory in JavaScript Engine

As depicted in the above image , the JavaScript engine consists of a call stack and a heap memory. The call stack is the spot where all the code gets executed then and there without waiting as and when they get pushed. On the other hand, heap memory is the spot where the engine allocates memory for objects and functions at run time as and when it is required. As mentioned before, our browser contains web APIs such as setTimeout, DOM APIs, console, fetch etc. and the engine can access these APIs using the global window object. Next, we have the event loop which sort of serves like a gatekeeper that picks functions such as setTimeout inside the callback queue and pushes them onto the stack as they require a certain amount of waiting time.

In the above example of the setTimeout function, when the function gets encountered, the timer gets registered in the callback queue. Next, the rest of the code gets pushed as usual into the call stack and gets executed. When the timer expires, the callback queue then pushes the callback function which was registered in the setTimeout function into the call stack and it thereby gets executed after the specified time.

Callback Hell

The phenomenon which happens when we nest multiple callbacks within a function is called a callback hell. The shape of the resulting code structure resembles a pyramid and hence callback hell is also called the “pyramid of the doom”. It makes the code very difficult to understand and maintain. It is most frequently seen while working with Node.js. The code below shows an example of a callback hell.

To understand the internal working of callback hell in javascript, assume we have to achieve a task say D. To achieve task D, we need to execute a series of dependent tasks ranging from A to C asynchronously. In short, first we execute task A, we get the result and task B need to depend on result A to start its execution. Similarly, unless result B is produced we can't execute task C. This leads to the chaining of tasks from A to D, which creates a set of nested callbacks.

In the example shown below, getUserData takes in an argument that is dependent or needs to be extracted from the result produced by getArticles which is inside user. The same dependency can be observed with getAddress also. This scenario is termed callback hell.

Now let's discuss various ways to escape the callback hell in javascript.

Promises

A promise is an object with a syntactical coating introduced by ES6 which became another way of skipping writing callbacks to implement asynchronous operations. Web APIs like fetch() are implemented using the promising methodology. It makes the code more readable and is a solution to escape the callback hell.

Promises generally have three states :

  1. Fulfilled: the desired action has been resolved or completed successfully.
  2. Pending : the desired action has neither been resolved nor been rejected and still in its initial state.
  3. Rejected : the desired action has been rejected causing the desired operation to fail.

Unlike synchronous functions, asynchronous functions may not return a value immediately. Hence, these asynchronous functions instead return a Promise() which can be later used to get the final value when it gets available in the future. We can easily handle situations where we successfully receive the data using the resolve() or where we fail to get the data due to some network error, timeout etc using the reject() as shown below.

The code below shows an example of how promises are written. If we observe we can see that it reduces the complexity of the code by avoiding the nesting of callback functions we saw in callback hell. Promises offer us .then() and .catch() to handle operations that gets completed or fails. The two examples shown below illustrate the same.

This is an example of writing a promise.

In the above example, to give the getArticle function a promise functionality, we return a new Promise(). Now, when we invoke the function by passing an id equal to 1, a new promise is returned by the function. Next, the timer waits for 5 seconds and prints Fetching data.... on the console. Since in the function invocation we have chained a .then() constructor, we can show the resolved output as a response. Hence, the console displays {id: '1', name: 'derik'} immediately.

This is another example showing a more practical example.

In the above example, let us assume that the functions getArticles, getUserName, getAddress are functions executing something similar to making an API request that returns a promise. So, first, we pass 10 as an argument to getArticles which returns us a promise. When this promise gets resolved, the desired data is extracted from the resolved data and passed as an argument in the getUserName function. The same process is repeated with the getAddress function also. Finally, once we get the final data, we print it in the console. So far, these steps involved chaining with the then() while if the promise in the final function call gets rejected, to address that scenario, we have chained it with a catch() where the error will be printed on the console if the promise gets rejected.

Async/Await

This is the second method to escape the callback hell. It serves as a better way of approaching promises because we are removing the usage of .catch() and .then() which again depends on callbacks. Async/await acts as a syntax sugar built on top of promises. It internally resolves promises as well as makes it easier for developers to write readable code. But it cannot be used with normal callbacks. It can be carried out by adding an ‘async’ before the function keyword. Though internally it creates chaining, it’s better than using promises as it enhances the understandability of the code and also prevents code blocking.

Since we are not using the .catch() (like in promises), we can write the async/await statements inside a try/catch block which is a better way of error handling. This is frequently used for asynchronous applications such as fetching data from an API etc. Avoiding the .then() and .catch() methods makes debugging easier for developers as well.

The await keyword must be written inside a async function and it essentially waits for a Promise(). The async function is made to stop its execution until a Promise() is either fulfilled or rejected. The function will resume only once the promise is settled. After it resumes, the value of the set of await expression will contain the value of the settled Promise(). await expression shows the rejected result if the Promise() gets rejected.

The following table describes the advantages and disadvantages of using async/await.

AdvantagesDisadvantages
Improves the performance of web applications especially when long running tasks are involved.async/await won't run on old browsers or environments.
Enables us to write less code as compared to Promises.Can be difficult for beginners to understand the underlying concept.
Helps us to write readable and maintainable code

Escaping the Callback Hell

In a nutshell, the usage of promises and async/await serves as a way to escape the callback hell as mentioned above. Apart from these writing comments and splitting the code into separate components can also be tried out. So, currently most the software engineers prefer using the async/await while building applications.

Conclusion

To sum up,

  • Functions that are passed as arguments to other functions and executed later inside the outer function are called callback functions. We commonly use them in addEventListener, and array methods like filter, map, etc.
  • JavaScript is a single-threaded, synchronous language. Synchronous callbacks are blocking in nature. For example, the array methods have to wait for the callback function to finish for them to finally complete their execution.
  • On the other hand, JavaScript can be made to behave asynchronously. For example, we can observe the asynchronous behavior of JavaScript by implementing the setTimeout function provided by he Web the APIs.
  • Callback hell is a phenomenon that happens when multiple callbacks are nested on top of each other. The two common ways of escaping the callback heare are by using promises and async/await.
  • Promises mainly have three stages such as resolved, rejected, and pending. It makes the code more maintainable and understandable.
  • Async/await is a better way than promises to resolve the callback hell situation. By removing the .then() and .catch() methods of promises, it uses async/await keywords which not only reduces the code complexity but also makes the error handling easier as it can be written within a try/catch block.

玻璃钢生产厂家东莞玻璃钢苹果雕塑长春火烈鸟玻璃钢雕塑公司东莞玻璃钢家具雕塑九江玻璃钢雕塑价位商场秋冬美陈门头布置辽宁玻璃钢人物雕塑设计上海卡通玻璃钢动物鹤雕塑泸州玻璃钢广场雕塑定制开封景观玻璃钢景观雕塑生产儿童玻璃钢雕塑设计公司南昌欧式玻璃钢雕塑哪家便宜西山区玻璃钢雕塑加工厂家玻璃钢雕塑低价甩卖黑龙江玻璃钢广场雕塑天津人物玻璃钢雕塑销售厂家浙江玻璃钢雕塑工艺品北京大型商场美陈销售珠海工艺玻璃钢人物雕塑吉林抽象玻璃钢雕塑图片湖北多彩玻璃钢雕塑供应商节庆玻璃钢雕塑生产厂河南商场美陈哪家好玻璃钢动物雕塑低价甩卖周年庆典商场美陈销售厂家横县玻璃钢雕塑厂家黑龙江公园玻璃钢花盆玻璃钢雕塑容易断吗玻璃钢雕塑frpmade青海仿铜玻璃钢雕塑定制文具店卡通玻璃钢雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化