To understand the above-mentioned example, firstly we need to understand two important terms: Execution Context and Call Stack.
The Execution Context
The Call Stack
Now is the time to understand the above given example as we have introduced you to the important terms that are going to be used in this explanation, such as Call Stack and Execution Context.
Refer to the below-given Stacking process in the Call Stack.
Note The setTimeout() method calls the callback function after the specified timer expires. The timer or delay accepts time in milliseconds.
1 second = 1000 milliseconds,
2 seconds = 2000 milliseconds, and so on.
In the above-given example, we have two callback functions: One is an anonymous arrow function inside the first setTimeout() and the other is the normal function inside the second setTimeout().
We have set the timer to 2 seconds (2000 milliseconds) in both of the setTimeout(). When the program executes, "I am from setTimeout" gets printed inside the console two times after the timer of two seconds expires, one from the first setTimeout() and another from the second setTimeout(). So now it is clear what the callback function is, but another arises here, and that is Why is it called "callback function"?
It is because the callback function is called back after some time in the parent function when needed, and then it is executed. It is important to know that the callback functions are very powerful as they provide access to the whole asynchronous world in a Synchronous Single-threaded programming language that has only one call stack in which the code is executed in a specific order. Let us see an example of async callbacks.
The Web APIs
What is Blocking?
Let us understand with an example.
In the above-given example, you can see that we have used the setTimeout() method, which is a part of Web API, the code is executed asynchronously which eventually results in the unblocking of code.
During the code execution, a call is made to fetchData(), so it’s pushed to the top of the call stack, then the setTimeout() function(which is inside the fetchData() function) is called and it’s pushed to the top of the stack. The setTimeout() method has two arguments: A callback (function) and delay (in milliseconds). Now the setTimeout() method starts a timer of 3s in the web APIs environment. After this, the setTimeout() is popped out from the stack. Then someFunction() is pushed to the stack, executed, and then removed from the stack after its completion. During this process, the timer(3-second timer) that was started by the setTimeout() method expires, due to which the callback function is pushed in the message queue. But here's a twist, the execution of the callback is not so quick and that's because of the event loop.
But what is it? Let's have a look.
So here comes a very interesting part of our article: the event loop.
Let us understand this by an example.
During code execution, a GEC is created, and the first code is executed immediately, printing "I am start" in the console. The subsequent code is pushed to the call stack and executed, logging "I am end". Meanwhile, a timer starts with a duration of 4 seconds. After the timer expires, the callback registered in the Web APIs environment is ready for execution. The callback is sent to the callback queue, and the event loop checks if the call stack is empty. If it is, the callback is pushed to the call stack and executed. The Event Loop's main task is to ensure the callback enters the call stack when it's empty. The callback queue exists due to the presence of multiple callbacks in a program. It ensures that callbacks are executed in the sequence they occurred in the queue. There is also a microtask queue for promises, where callbacks have priority over the task queue's callbacks. Note: The microtask queue prioritizes the execution of callbacks over the task queue.
Working of Promises
A Promise is an Object which has three states: Pending state, Resolved state, and Rejected state. As the words suggest, when the promise is not made yet, the state is called a Pending state. When the promise is successfully fulfilled, the state is called Resolved state, and when the promise fails to get fulfilled, the state is called Rejected state.
Refer to the image given below.
In the above diagram, you can see that the first state of the promise is the Pending state, then if the promise is resolved, .then(anyFunction) is called for asynchronous operations, and if the promise is rejected, the .catch(anyFunction) is called for exception(error) handling. The promise again goes into the pending state if there are multiple promises and the process of .then() and .catch()continues till the last promise.
We can use the Promise() constructor to create a new Promise object.
Example of Promises
Now we are going to see an example of promises which will eventually crystal clear your concept of Promises.
In the above-mentioned code, we have a promise called 'promIse' which accepts a function. we have passed an anonymous arrow function with two parameters: resolve and reject. Let us assume that there is some data that needs to be fetched from the server.
Now for simplicity, we assume that if a certain condition is met, then resolve the promise and if it does not meet, then reject the promise. We have set the value of the variable 'anyCondition' to be false, as the condition is not met due to being false, the else block gets executed, and the promise gets rejected hence the .catch() method is executed. We have console logged the message in the .catch() that we received from the reject in the promise. Hence 'Promise is rejected' gets printed in the console. Similarly, if the condition is set to be true, the promise will be resolved, and hence .then() method will get executed, and 'Promise is resolved' will be printed in the console.
If you notice the code, we have attached the .then() method to the promise and .catch() method to the .then() method. This direct attaching of methods is called 'Chaining'. Chaining, as the name suggests, simply means to chain the callbacks. Hence, In Promises, the callbacks are chained, which eventually makes the code more readable and easier for debugging.
|In Promises, the callbacks are chained, which makes the code more readable||Passing Callbacks directly leads to a state called 'callback hell', which makes the code difficult to read|
|Promise callbacks are called in the strict order of occurrence in the microtask queue||Callbacks are not always called in the strict order of occurrence in the task queue|
|Promise callbacks are given priority while execution as they are stored in the microtask queue||Callbacks are not given priority while execution as they are stored in the task queue|
|Error handling is really good with Promises||Error handling is not that good with Callbacks|
|Promises do not lose the control of how the function will be executed when passing a callback to a third-party library||Callbacks lose the control of how the function will be executed when passing a callback to a third-party library|
A: Callbacks are functions that are passed as arguments to other functions and are executed at a later time or when a specific event occurs. In asynchronous programming, callbacks are commonly used to handle the results of asynchronous tasks, such as network requests or timer-based operations, ensuring that the code executes in the desired order.
- Blocking is very dangerous and affects the user experience badly.
- Browsers can communicate with the outside world with the help of various Web APIs.
- Browsers combine the power of Web APIs with the call stack.
- Console and fetch() are also part of Web APIs.
- The main task of the event loop is to check the emptiness of the call stack, and if it is found to be empty, it allows the callback to enter the call stack.
- Promises are stored in the microtask queue and are given priority over normal callbacks by the event loop.
- Callback hell can be avoided by using Promises.