Writing
Node.js0 min

Node.js threading internals

Node.js is one of the most popular runtime environment, in this article we clarify its threading nature, which often confuses beginners. We will explore its single thread execution, behind the scenes multi-thread work and built-in worker threads.

Node.js and JavaScript

Something that confuses a lot of people is the difference between Node.js and JavaScript. The simple explanation from a simple google search is that "Node.js is the runtime environment and JavaScript is the programming language" but what does that mean?

In September of 2008, Google had launched Chrome, the browser gained rapid popularity and one of the reason for that was its fast JavaScript execution engine called the V8 Engine. The V8 engine built with C++ was made open-source by Google, so a developer by the name of Ryan Dahl took this code and converted this to a runtime environment called Node.js. Simply put, Node.js is a program that lets you execute JavaScript in your machine (and not only inside browser) allowing you to create and run application built on JavaScript on your system. This led to creation of many popular frameworks and toolkits we know of today such as NestJS, Express and Electron.

Single Threaded Nature

When we talk of Node's single threaded nature we are talking about its JavaScript execution which occurs on a single thread called the main thread. This means at any given moment only one set of instructions is executing on a CPU's core.

To clarify this further we can take the example of an express application. If you have worked with express before you might have encountered an issue where the main thread gets blocked when a heavy computation is taking place on one of its route. This means all other incoming requests wait until the heavy computation is resolved and main thread is free again.

Node's Event Loop

Before getting into multi-threading we discuss node's event loop which will help you quickly grasp the concept of multi-threading in node. As we discussed earlier, by design JavaScript is single threaded which means there is a single main thread doing all the work. But what happens when this thread is dependent on an operation outside of its control, say file reading or writing or database query response? During the duration of this operation the main thread sits idle practically blocked until the request is resolved. The Event loop solves this.

Instead of waiting for an external operation to complete what the main thread does is registers a callback for the operation and delegates it, then moves on to handle other work. Once the operation completes, the result is placed in a callback queue, and the event loop picks it up and hands it back to the main thread to process

Multi-threading

As mentioned earlier, Node.js is built on top of Chrome's V8 Engine that is built with C++. Internally Node makes use of the library called libuv which by default contains at minimum four dedicated threads. When JavaScript is performing external I/O operations that can cause the main thread to be blocked or sit idle, it hands these operations via callbacks to these internal threads which return a response when an operation is done.

What is important here is to note that not all external operations are handed to the internal working threads, some are handled by operating system itself which informs node when a task is completed such as database queries, network requests or DNS lookups.

Worker threads

Heavy computation that happens synchronously can block the main thread, this means if you are performing an internal JavaScript operation that takes up time the main thread itself handle the operation execution until it is completed and only then moves on to the next task.

To prevent this blocking, in June 2018, worker threads were introduced. Worker threads let JavaScript create secondary threads to perform computations in parallel to prevent main thread blocking. Worker threads work by communicating via message channels, each worker sends a message which is serialized on send and deserialized on receive creating a new copy each time to keep threads isolated and avoid shared state conflicts.

Conclusion

In this article, we discussed Node's single-threaded JavaScript execution, how the event loop prevents the main thread from sitting idle during external I/O operations, and how libuv's internal thread pool handles those operations behind the scenes. We also covered worker threads and how they enable parallel computation to prevent main thread blocking.