Subtle differences between the JavaScript and Node JS event loops (Part 2)

Pandula Weerasooriya
4 min readJan 13, 2020

--

The first part of this article addresses the Asynchronous nature of JavaScript and how JavaScript handles asynchronous tasks using web APIs and a message queue. Although there are few differences, the key ideas remains the same in Node JS event loop implementation as well. We’ll look at some key differences in how Node addresses the event loop in here.

Unlike JavaScript tutorials though, Node JS tutorials tell upfront that Node is Single Threaded, which is the standard tagline in any book/tutorial/MOOC about Node JS. However, this statement should be thoroughly investigated.

How Node JS works behind the scenes

If you look at the right most part of the above image, there’s a stack of worker threads which are available at Node run-time can be seen. Which, questions the popular belief of single threaded nature of Node JS.

Basically, Node JS is not a new programming language or another JS framework/ library. It is a run-time environment that gives JS access to do common things that other programming languages do such as , accessing the file system, I/O, Networking etc. But before we dive in to the weeds, I’d like to talk about two fundamental elements of the Node Architecture.

  • V8 Engine — This is an open source JavaScript engine created by Google, which compiles JS code in to c++. This is what makes it possible to access JS code outside the browser.
  • Libuv — This is also an open source project written in c++, that gives Node access to the operating systems, underlying file system, access to networking and also handles some aspects of concurrency as well.

Node Event Loop

Let’s look at a higher level overview of the Node JS event loop implementation. Unlike, the JS event loop, there are 3 tasks that Node looks at in a specific order.

  1. Node looks to see if there’s any pending timers. Which are set using functions such as setTimeout and setInterval.
  2. Then it checks whether any pending OS tasks are left. A server listening to a port is one of them.
  3. Finally it checks, if any pending long running operations such as file I/O is running behind the scenes.

Node pauses execution at this stage, and it continues when

  • a new pendingOSTask is done
  • a new pendingOperation is done
  • a timer is about to complete

It is important to note that a node application actually terminates when the event loop can’t find any pending tasks.

Is Node JS single threaded?

Well, in simple terms, Node’s event loop is single threaded. However, some of Node framework/standard library are NOT single threaded. Some of node modules such as fs (file system), crypto (cryptographic functions) have functions that use multiples threads. Let’s look at a code sample to cement this new found knowledge.

The above code is pretty simple as it hashes a string using the powerful “sha512” algorithm and it records the time it took to do it.

To experiment the multi-threaded nature of this program, you can comment out the final 4 calls and only run the first call. This registers on my machine a 963ms of time. Which means that it takes around 1s to do this encryption.

After that, I commented out only the last call and ran the first 4 calls. Here’s the result.

Process output for the first 4 function calls

You can see that, all the processes took roughly around 1s. If this process was single threaded, this should have taken around 4s!. After this, I ran the whole 5 hashing calls. Which yields in an interesting result.

Process output for 5 function calls

Now this time, the code has taken nearly 2s, which is the double the amount of the time it takes to run one encryption. The reason for this is that, by default, node has a thread pool of 4 threads and the therefore the first 4 crypto calls ran in parallel and the fifth call ran after that. You can change the number of threads manually by adding

process.env.UV_THREADPOOL_SIZE = 2

at the start of the file. However, in Windows this won’t work and you’d have to set an environment variable before running your node app like below,

set UV_THREADPOOL_SIZE=2 & node app.js

So, in a nutshell that’s how the node js event loop is implemented. There are some other exceptional behavior as to how Node handles set-immediate calls and close events in the event loop, however, they can be a bit confusing to digest and are not very helpful in understanding key Node concepts.

As per the title of the article, a summation of the differences of the two event loop implementations are given below.

  • Web APIs provides the asynchronous events such as DOM events, timeouts, API calls to the JS runtime and in Node, Libuv library mostly handles those.
  • Node uses a thread pool with a default of 4 threads to handle I/O, Network etc. tasks efficiently.
  • There’s no order in which events are handled in JS. However, Node looks at processes in order (timeout, OS processes etc.)
  • The program doesn’t terminate when there are no pending tasks in JS event loop, however, Node terminates the program when there are no pending tasks to be handled by the event loop.

--

--

Pandula Weerasooriya
Pandula Weerasooriya

Written by Pandula Weerasooriya

A fullstack engineer who's passionate about building data intensive products and distributed systems. My stack includes Golang, Rust, React, NodeJS and Python.

Responses (1)