Understanding the ever running EVENT LOOP

In-depth understanding of event loop and its friends

ยท

6 min read

Understanding the ever running EVENT LOOP

Hello everyone around the world ๐Ÿ’œ๐Ÿ’œ๐Ÿ’œ

I am Dipen Chavda and I hope when you read this article you are well and healthy, here we are again going to go on a ride to understand the concept of EVENT LOOP in JS. This is a very important concept when it comes to understanding the asynchronous behavior of Javascript and how JS makes asynchronicity possible while being a single-threaded programming language.

Before all that first we need to understand how does JS execute a program behind the scene...

OK then let's take a piece of code...

function fun1(){
    cosnole.log("fun1");
    fun2();    
}
function fun2(){
    console.log("fun2");
    console.log("end");
}
fun1();

Above is a simple JS code and in that, we are having few functions. Before understanding EVENT LOOP we need to understand how the call stack works in JS that is why I have chosen the code snippet with function calling other functions.

Let's understand the above code

We know that every time a function gets called then it creates an execution context, execution context has the function definition and the local variables related to that function.

blog-2-i-1.png

Looking at the above image we can see that when the function gets called it gets pushed into the call stack and it gets popped out when done executing that function and at the end when everything gets executed the GEC (Global Execution context) will also be popped off the stack and that would mark the end of the program.

Call stack will not wait for anything it will quickly execute whatever comes inside.
It waits for NONE. ๐ŸŽ๏ธ๐ŸŽ๏ธ๐ŸŽ๏ธ

Here comes the main character of our story: THE EVENT LOOP โœจโœจโœจ

blog-2-i-2.png

To understand the event loop let's have a small code snippet to get us started.

console.log("start");
setTimeout(function callback(){
    console.log("callback");
} , 1000);
console.log("end");

With this example, we will also get to learn how JS runs asynchronously.

Let's have a code walkthrough:

Starts from console.log("start") after that we are setting a timer of 1000ms and after that setTimeout() is going to call the callback function when the timer expires but here call stack will not wait for 1000ms and then execute the later part of the program it will directly jump to console.log("end") and will execute it, something very odd is happening here which is that the call back function getting called later and the call stack is empty, the call stack being empty means that we have run the program now call stack does not have anything left to run but still somehow after 1000ms it gets refilled and it runs that piece of code.

Let's solve this mystery piece by piece ๐Ÿง 

Starting with, If the call stack is empty then where is that call back function getting stored? we need to first know that before moving to how will it reappear in the call stack.

Ans : Browser memory

blog-2-i-4.png

now after the timer of 1000ms expires this call back function that we have stored in the browser memory must reach the call stack to get executed but we cannot directly get there. To do this we have the 2 key components working together in the entire program execution life cycle. The Event loop and the call back queue.

blog-2-i-5.png

The dynamic duo at work ๐Ÿ”ฅ๐Ÿ”ฅ:

So when the timer expires the function stored in the browser memory will be pushed to the callback queue and it will wait there until the event loop gives it a green flag ๐ŸŸข to move forward and jump in the call stack then as usual call stack will execute that super fast ๐ŸŽ๏ธ.

The event loop will be keep checking both the ends (call stack and the callback queue) constantly. Whenever the event loop finds out that the call stack is empty then it will contact the callback queue and then push the function from the callback queue to the call stack one by one if we have multiple functions inside the callback queue.

Any type of call back that we have, it will go through the same process like if we have an event listener and which reacts to click events then whenever the user will click the call back function will leave from the browser memory and go through the whole process that we just talked about.

So far so GOOD...

The microtask queue

This is the different type of queue that we have other than the callback queue which works with the event loop in a tight bond.

We always start with a code snippet so let's do that...

console.log("start");
setTimeout(function cbTimer(){
    cosnole.log("CB setTimeout");
} , 1000);

fetch(api url)
.then(function cbFetch(){
    console.log("CB fetch");
});
console.log("end");

Whenever we are working with promises and when that returns a callback function then that callback function does not go to the callback queue but it goes to the microtask queue.

Microtask queue has greater priority in execution than callback queue.

Microtask queue > Callback queue.

Now let's look at the execution of the above code snippet...

blog-2-i-7.png

Code walkthrough

Here we have two parts of code in which we have callback functions. First with setTimeout() and other is a promise with fetch() If you wanna learn about promises then check out my blog on in-depth understanding of promises with fetch myblog. First, both the console.logs are implemented after that we go figure out what do we wanna do with callbacks.

First, we approach the setTimeout() one and we set a time in the browser with 1000ms, and as we know that the callback will be stored in the browser memory and it will be pushed into the callback queue after the timer expires. So that is waiting in the memory then we move forward and encounter fetch(API url) it also has a callback function and that function will be waiting in the browser memory and request must be made to the web servers and now that callback will be waiting in the browser memory and when we get the response from the web servers then that callback will be pushed into the microtask queue.

Now the event loop will be keep checking both the queues and the call stack, first, it will check the microtask queue and if the call stack is empty then the callback will be pushed in the call stack and after that again call stack will be empty then the callback from the callback queue will be pushed inside the call stack and get executed super fast.

Callback queue starvation

blog-2-i-8.png

Here in the above image we have the microtask queue that keeps filling up then even if the callback inside the callback queue is eagerly waiting it won't be able to get executed because the event loop will give more priority to the microtask queue and first focus on emptying the microtask queue and after that event loop will focus on the call back queue so the callback inside the callback queue will be keep waiting if the microtask queue does not get empty.

This will lead to the starvation of the callback queue and it would take more time to execute that callback than expected.

We did it!!!

After a long journey with the event loop and its friends, we are finally here. I hope that you guys liked this article and your feedback will be highly appreciated.

If you wanna check my previous article about promises with fetch then check that out here

Share this article with your dev friends and share it on Twitter and if you wanna contact me or ask me anything then I am right here on my Twitter

ย