As we know that JavaScript Engine has just a single call stack which means that it can execute one statement at a time and executes it immediately and does not wait for anything. But if we really need to wait for sometime before executing the further code. Here comes the role of Callbacks.
Let’s understand with the help of an example, in which we’ll have a setTimeout of 5 seconds and a callback function passed to it.
console.log("Start");
setTimeout(() => {
console.log("5 seconds");
}, 5000);
console.log("End");
Here, it is the job of setTimeout to execute this callback function after 5 seconds. So callbacks is a powerful way to do asynchronous tasks in JavaScript.
Let’s understand callbacks with the help of the example of an E-Commerce Website.
console.log("Start");
const cart = ["shoes", "pants", "sunglasses"];
api.createOrder(cart, function(order) {
api.getPayment();
})
console.log("End");
So, here we have a cart in our website and we have some items inside it and there exists a createOrder
api which is used to create the order and when the order is successfully created then only the getPayment
api is called which is used to complete the payment. There is a dependency between both of them as getPayment
api is dependent on the successful execution of createOrder
api. So here callbacks comes into the action as it is the responsibility of the createOrder
api to create the order and execute the callback function which calls the getPayment
api.
Now, let’s make this program more complicated, suppose there is a orderSummary
api which has to be called once the getPayment
api has successfully been executed and then an updateUser
api which has to be executed only when the orderSummary
api is executed successfully.
console.log("Start");
const cart = ["shoes", "pants", "sunglasses"];
api.createOrder(cart, function(order) {
api.getPayment(order, function(payment) {
api.orderSummary(payment, function(summary) {
api.updateUser(summary, function(user) {
console.log("User updated:", user);
})
})
});
})
console.log("End");
That's how we can manage this dependency between this whole flow, which happens one after the other, but do you see a problem over here? So when we have a large code base and there are so many APIs here and there and APIs are dependent on one after the other, we will end up falling into a Callback Hell or Pyramid of Doom.
What is a Callback Hell ?
Callback Hell refers to a situation in programming, particularly in JavaScript, where multiple nested callbacks make the code difficult to read, understand, and maintain. It usually occurs when asynchronous operations, such as database queries, API calls, or timers, are chained together in a deeply nested structure. When functions depend on the result of asynchronous tasks, callbacks are used to handle the completion of each task. However, if each task depends on the result of the previous one, it can lead to deeply nested and complex code.
Callbacks also cause a problem known as Inversion of Control (IoC)
What is Inversion of Control ?
When you write a callback, you are giving the control to another function or framework to call your code at the appropriate time. This is an example of IoC, as you no longer directly control the sequence of operations. Instead, the control lies with the function or library you pass the callback to. It can be risky as there as possibilities that the function does not calls the callback function or it might even call it twice.
So, we can solve these problems with the help of Promises in JavaScript.