NodeJS: Its Async Nature and the Event-Loop

Saptarshi Dey
5 min readApr 23, 2020

--

To start with, let’s ask this question — “What is Node Js ?”

Well, most people have googled this, especially those who are relatively new to Node JS and the answer we get is mostly like this:

“Node Js is a single-threaded, asynchronous non-blocking I/O model that executes JavaScript on the server-side”

So it basically means the code we write is executed on a single thread.

Being single-threaded means it has a single call stack and it can do only one thing at a time. Then how does NodeJs perform async non-blocking I/O operations simultaneously?

Node JS is built on Chrome’s V8 JavaScript engine. Along with the V8 runtime which consists of a heap memory and a call stack, the NodeJS environment consists of a callback queue,

some other APIs, and the Event Loop. All these work together to achieve the asynchronous non-blocking nature of NodeJS. We will see in detail by running code snippets to see how the NodeJS environment makes use of all these to achieve is asynchronous nature.

So before we dig deep, one thing is worth mentioning that NodeJs is not fully JavaScript. It has a fair amount of C++ code too.

Yes, you read that right. NodeJS contains C++ code. As I mentioned in the previous paragraph, NodeJs environment has some other APIs, these APIs are nothing but the C++ APIs.

Here is a block diagram that represents the NodeJS architecture:

The JavaScript code we write, the JavaScript that is part of NodeJs, V8 itself, and the Event Loop all runs in a single thread, the main thread. This is what we mean when we say NodeJs is single-threaded that all these things are running inside a single thread. So if in our code there is a JavaScript method which is synchronous and it calls some C++ API then that C++ code will always run on the main thread, whereas if there is a JavaScript method which is asynchronous and backed by a C++ API, then sometimes it doesn’t run on the main thread.

Now the question is if it doesn’t run on the main thread then where does it run? NodeJS is single-threaded

First let’s see how the following snippet of code executes

Here we will be using the crypto module. The crypto module contains some methods which can be accessed both synchronously and asynchronously

This is the synchronous implementation of the pbkdf2 method in crypto module. We are calling the method two times within a loop. The code generates output as follows:

So, as we see the execution time for the above piece of code is almost 319 ms

Now, let us see another code snippet

Here we have the asynchronous implementation of the pbkdf2 method. Here also we are calling the method two times inside a loop. The output for this is:

This code took about 191 ms to execute.

So, we can see the synchronous implementation took almost double time to execute than the asynchronous version.

Let’s look into our synchronous implementation. Here the pbkdf2 is called two times. Now as both of these are synchronous calls the code always executes on the main thread. First, it gets inside the loop then executes crypto.pbkdf2Sync() once. Now it is a blocking call, so the execution waits. After this execution is completed, crypto.pbkdf2Sync() is called for the second time. Now also the execution waits. After the second execution of the method is completed the console.timeEnd() line is executed at the end.

Well, this was pretty straight forward. The whole program execution was completed on a single thread.

Here comes the asynchronous implementation. How does it take so less time to execute?

One thing most of us know that here also the crypto.pbkdf2Sync() method was called two times but they executed in parallel. Well, NodeJS is single-threaded, how does then this parallel execution takes place?

Let us go back to the NodeJS runtime architecture diagram. All the things mentioned there comes into the picture in the execution of the asynchronous implementation of our code. Here when we enter the for loop it calls crypto.pbkdf2() from the main thread. Now as I stated earlier, any asynchronous method backed by C++ will not run on the main thread. This is handled by the C++ APIs. Now C++ APIs have access to threads and thread pools. As this crypto.pbkdf2() is backed by C++ code, what C++ does is it assigns a different thread to this method and the method continues running on this thread. Now, the main call stack is empty. It again calls crypto.pbkdf2() from the main thread and the same thing happens. This time also a separate thread is assigned to the method and it executes concurrently with the first call.

After the execution of either one of these functions is completed, the callback provided to it is pushed to the callback queue. Here when both the crypto.pbkdf2() calls complete execution, the callback queue will have two callbacks.

After this, the event loop comes into the picture. The event loop basically acts as a dispatcher and it dispatches the callbacks from the callback queue to the main call stack one by one. There what is inside the callback is executed. Here its prints the Execution Time.

So, in this way, NodeJS manages to do stuff concurrently. Also, some of you might think after this is that NodeJS is not so single-threaded.

To say things right NodeJs Runtime is single-threaded, but in the background, it uses multiple threads to execute asynchronous code.

Thank you for patiently reading this article and I hope this has helped you gain some knowledge.

--

--