Event Loop in JavaScript

Vinayak Soni
GAMMASTACK
Published in
7 min readApr 6, 2021

Hello geeks, today we are going to understand a very untouched, but important topic in JavaScript i.e. Event Loop. Believe me, after reading this blog completely, you will get a full understanding of the Event Loop and its working.

Prerequisites:

Well, to understand “Event Loop”, you just need to have a basic understanding of the JavaScript language.

Boiler Plate:

  1. Single-threaded synchronous language
  2. Blocking and Non-blocking statements
  3. Browser’s Web API’s

Let’s dive into these.

  1. Single-threaded synchronous language

As you all might know JavaScript is a single-threaded synchronous language. Well, what does that mean?
Single-threaded: It means JavaScript has only one Call Stack and one memory heap.
Synchronous: It means JavaScript can run only one statement at a time.

2. Blocking and Non-blocking code

Blocking code: The next statement can’t be executed until the current statement gets executed completely.
For example,

Blocking code example

Non-blocking code: The next statement can be executed while keeping the current statement aside for some time.
For example,

Non-blocking code example

Note: See, the output of the non-blocking code example carefully.

Note: If you didn’t understand the non-blocking code example. Don’t worry, we will deep dive into this later.

3. Browser’s Web API’s

Browser provides some web API’s to the JavaScript engine to make use of its features like timer, location, console, etc. Browser wraps all these features into a single global object “window” and makes that global object available to the JavaScript engine. Now, using this window object, the JavaScript engine can access the browser’s features.
For example,

Browser’s Web API call example using window object

Note: As a window object is a global object and it is available to the JavaScript engine, therefore we can directly use console.log() despite window.console.log().

Note: setTimeout() is also one of the browser web API’s, through which we can access the timer feature of the browser.

Main topic (Event Loop):

Now, as we have set up our boilerplate, we will a get better understanding of the Event Loop.
I know it’s getting a little longer, but just bear with me for some more time and you will surely go with a complete understanding of this topic.

Whenever the program gets executed, the Global Execution Context is created and pushes it to top of the Call Stack. And when the function is called, the Function Context is created and pushed to the Call Stack. As soon as, it gets completely executed, it will be popped out from the Call Stack.

Global Execution Context: It is just an environment where JavaScript code is executed and evaluated.

Call Stack: It is used to manage execution contexts.

For example,

Let’s see what happened when the above code gets executed.

As I mentioned earlier, the Global Execution Context will be created for this program and pushed to the top of the Call Stack.

Call Stack current state

*GEC: Global Execution Context

Now, the first line gets executed. As it is the function declaration, the function gets stored in the memory. When the program execution reached line 5, printHello() is called. Hence, it will be pushed to the Call Stack.

Call Stack and Console current state

When the printHello() executes, “Hello” will be printed in the Console and when it completes its execution, it will be popped out from the Call Stack.

Call Stack and Console current state

Now, the execution comes to line 7 and “Bye” will be printed in the Console.

Call Stack and Console current state

Now, as the program is executed completely. The GEC is popped out from the Call Stack.

Call Stack and Console current state

This how the Call Stack manages the execution context.

Now, we have a better understanding of how the Call Stack works and how the GEC is pushed and popped out from the Call Stack. So, that’s it. Isn’t it?

But wait….

What if some code needed to be executed after some time. What will happen then to the Call Stack? Will the remaining code waits for the previous code to get executed?

PAUSE HERE, and think of answers to these questions.

So, let’s have one example of how the Call Stack and GEC reacts when some code needed to be executed after some time.

Let’s see what happened when the above code gets executed.

As I mentioned earlier, the Global Execution Context will be created for this program and pushed to the top of the Call Stack.

Call Stack current state

Now, the program execution starts from line 1. And, it will print “Welcome to program” in the Console.

Call Stack and Console current state

Now, the program execution moves to line 3, where setTimeout() function is written. An interesting thing will happen here. The callback (printHello()) written inside the setTimeout() will be registered in the browser and the program execution will move to line 7. (As I mentioned earlier, JavaScript is a synchronous single-threaded language. So, it will not wait for any code to be executed after some time). What happens to this setTimeout() callback? We will see it later. As the program execution moves to line 7, “Bye!” will be printed in the Console.

Call Stack and Console current state

Meanwhile, we are executing our code, the timer is still running in the background. As soon as the timer expires, the callback function which was registered needs to be pushed in the Call Stack and gets executed. But, how that will be handled?

As our program is reached to its end, the GEC will be popped out from the Call Stack.

Call Stack and Console current state

Let’s see…

As soon as the timer gets expired, the callback function comes into the Callback Queue. You must wonder, what is this Callback Queue?

Callback Queue: It is just a normal queue where all the registered callback functions wait for their execution. It works on the principle of FIFO(First In First Out). It contains tasks like setTimeout, setInterval, I/O, etc.

Now, our main topic i.e. Event loop comes into the picture. Event Loop has only one job, i.e. to continuously monitors the Callback Queue and the Call Stack. Whenever Call Stack gets empty, Event Loop will push the callback functions waiting inside the Callback Queue to the Call Stack. As soon as the callback functions pushed to the Call Stack, their execution gets started.

Event Loop

In our program, after 2 seconds the printHello() function pushed in the Callback Queue. The Event Loop has been continuously checking the Callback Queue and the Call Stack. And, now the Call Stack is empty, therefore the Event Loop will push the printHello() function present in the Callback Queue to the Call Stack.

Call Stack and Console current state

printHello() function starts its execution and prints “Hello” in the Console. As the execution completes, the printHello() function will be popped out from the Call Stack and the program execution ends.

Call Stack and Console current state

This is how the Event Loop works. Well, there is one more queue called Microtask Queue.

Microtask Queue: It is just another queue where tasks like Promises, MutationObserver, queueMicrotask, etc. after resolving resides and waits for their execution.

Microtask Queue has more priority than Callback Queue. If there are tasks present in both Microtask Queue and Callback Queue, then Event Loop will first push the tasks present in Microtask Queue to the Call Stack. After that, Callback Queue tasks gets pushed one by one.

That’s it. I hope you get a complete understanding of Event Loop and how it works. You have come along with me till here, then don’t forget to give the thumbs up 👍 if you found this useful.

--

--