Before we begin
I assume you have the basic understanding of Asynchronous JavaScript and the use of setTimeout API. If not refer this article.
We know JavaScript is synchronous single threaded language. But it is able to manifest itself asynchronously through different ways like using callbacks, promises etc.
Let's take a small and simple code snippet
Let's understand the flow of the above code. JavaScript executes line by line. First, the console.log("start") statement is pushed onto the callstack, and we get "start" printed in the console. The callstack can execute only one statement at any given time.The second statement we have is setTimeout, setTimeout is not a part of JavaScript, it is one of the many APIs which the browser provides. setTimeout conatins a callback function which needs to be executed after 5 Seconds.
But our callstack does not have access to timer and it has no power to delay the execution. Callstack executes whatever is pushed onto it immediately and have no power to stop, delay or perform any other operation. As a result JavaScript Engine access the webAPI to complete this operation. In the mean time the third statement console.log("End") is pushed onto the stack and we get "End" in our console.
After 5 Seconds the callback function from setTimeout is pushed onto callback queue. Now the event loop take this and pass onto callstack to execute it. So, our final output for the above code is
The primary function of event loop is to check is callstack empty and if it is then, push the callback functions, to execute them.
Callstack, web APIs and event loop all have independent works, there is a good coordination between them and this gives JavaScript super powers.
Let's take another example and I want you to predict the output
Applying our knowledge, we can expect output to be
- Start
- End
- In setTimeout
- In fetch
or
- Start
- End
- In fetch
- In setTimeout
But which one is right ! . You might consider the first one to be the output as per the flow of the code. fetch is called only after setTimeout. But it is not true. The correct output is the later one. That's where the Microtask Queue in JavaScript kicks in.
We know the JavaScript engine contains a call stack where one JavaScript statement is executed at a time, and callbacks are sent to the callback queue for later execution. But, not all callback code is put in the call back queue. Some callback statements have higher priority than others. Let's understand what it mean.
The above code snippet contain four statements
- Two console.log statements
- setTimeout method that have callback function setTimeoutcallback
- fetch method with callback function fetchCallback
The first console.log statement is pushed onto callstack and they get executed, so we get "Start" in console.
The second and third statements, setTimeout and fetch are the webAPIs and both have a callback function to execute. Both of them are pushed out of the main flow and control moves to fourth statement. Again callstack execute this statement, now we have "start" , "End" in our console.
As per the order in code snippet, the JS engine considers setTimeout and sets a timer for 5 seconds and after which the callback function is sent into callback queue.
The third statement which is a fetch call, forms the part of webAPIs. We get a Promise after executing the fetch method, depending upon the state of the promise .then handlers are executed. For now, let us consider the Promise to be in fullfilled state. Now we need to take care of the callback function fetchCallback. This time JS engine won't push the callback function to callback queue. It push fetchCallback to Microtask Queue. The below figure, representing JavaScript runtime environment helps you to understand better.
Microtask Queue is very similar to Callback Queue, the only difference is that Microtask Queue have higher priority than Callback Queue
What does "Higher Priority" mean here ?
It means that suppose we have callback functions in Microtask Queue and Callback Queue, then Event loop takes callback functions from microtask queue and push them to callstack for execution, Callback functions from Callback Queues are taken only after all callback functions in microtask queue is exhausted. Event loops is very partial towards Microtask Queue. Even though there is 1 callback in microtask queue and 10 callback in callback queue, Event loop looks for callback functions from microtask queue.
But, what kind of callbacks are pushed onto Microtask Queue ?
Only callbacks from promises and Mutation Observers are sent into Microtask Queue. Mutation observer observers the DOM events and executes callback. This is one of the important feature in JS, we need to know.
Let's take another example
Apply the concepts which we learnt so far and predict the output for the above code snippet. Now, you are aware of Microtask Queue,and Fetch callbacks are given higher priority over setTimeout.
Hence, the output for the above code snippet looks like this
I request you to try out these examples, tweak around them by yourself, only then you get a clear idea.
If you have any question, feel free to comment below. Iโll reply each one of you as soon as possible.
Hope you found the article helpful.๐ If yes, consider connecting with me on Twitter , LinkedIn. If No, please leave a feedback below. I would love to read them and improve.
Till next Time, Happy Learning ๐
Veeresh B V