Is Node.js Single-threaded? How to Run the Requests in Parallel
Node.js is a single-threaded asynchronous JavaScript runtime. This means that your code will be executed in the same thread.
Such architecture is experimental and slightly different from other languages (such as PHP, Ruby, ASP.NET), where each client requests are instantiated on a new thread. This approach has proved itself in the systems with streaming and low latency in mind. Here you can find some great explanations on why node js in single-threaded.
On the other side, such architecture is not fully suitable for resource-intensive projects with a high load on a processor (complicated math calculations, large files processing ).
The good news is that Node.js is not entirely single-threaded. It allows you to operate your own threads and write multi-threaded solutions.
Just because Node is designed without threads, doesn't mean you cannot take advantage of multiple cores in your environment. Child processes can be spawned by using our child_process.fork() API, and are designed to be easy to communicate with. Built upon that same interface is the cluster module, which allows you to share sockets between processes to enable load balancing over your cores.
In this article, I will explain how to do multithreading in Node.js app development and show you practical examples.
Running multiple tasks in parallel
Not that long ago I had to implement a small algorithm. While I was doing this, all other parts of the program just “froze”. As the algorithm couldn’t be optimized, the only way out was to run the program in parallel.
This experience prompted me to think about other situations where one can use this approach. That’s why I’ve decided to write the article using a small algorithm as a basis, and show how it works both in a single and multiple threads.
Let's imagine that you have to process several or multiple files (parse, compress, filter, etc). They can be of any format (.json .csv .exel ). If your algorithm runs in a single thread and processes files one by one - you lose a lot of time.
Let's see the example. In this case, I use .wav files. My task was to take the maximum spectra of .wav files and write them in .json. The spectra are computed with the help of fft algorithm. It is resource-intensive enough, which makes it perfect for this article.
Here you can see how the algorithm works in both single and multiple threads. In a single thread, files are processed one by one, when in multiple - they are run in parallel and, as a result, much faster.
How to implement the algorithm in a single thread
Here are 3 main steps for implementing this algorithm:
- Read all the files in the directory
- Process the files in a certain way (decode or search for a spectrum)
- Note the result (in the file, database, etc.)
Below you can see the code for implementation of this algorithm in a single thread. The full code is available here.
With such an approach, the algorithm will process the files sequentially. If the processing of one file takes a lot of time, the more files you have, the more time it will take to process them.
Keep in mind that there is no need to parallelize threads if the tasks are not resource-demanding and take little time to process.
File processing in multiple threads
You сan implement file processing in a single thread in three ways:
- Run your code in a separate thread with the help of child process
- Use the packages for working with the thread from npm environment
- Use the class Worker threads (experimental for now)
By using child process you can run additional scripts or other js files as the separate threads. Your main thread can read the files and the additional thread will process them.
For me, this approach is not the best choice, as it looks like you are running several different programs. I haven't used it, but if you are interested, read more about it here.
To run the processing of one file in a separate thread, I have used threads package. It allows to run js code in a separate package and transfer the data from the main thread. Here is how our algorithm could look :
However, it's not as easy as it seems to be. You can't pass the function from the main thread into a child thread. Also you can't call require ('../some-code');
in the child thread.
So your request of decode
, fft
, spliceSpectrum
method will just call is not a function
. It means that all the methods decode
, fft
, spliceSpectrum
should be written inside the spawn(function(input,done)
. And, honestly, this is not the best architectural solution.
Fortunately, you can call npm modules in the сhild thread. The solution is to write a local package that will export the methods mentioned above in the child thread and allow you to use it at ease. Let's locate this package in the directory ./modules/fft-thread-woket
. Then let's initialize the package with the command npm init
. Now you can use it in the following way:
You can see the full code of the local package here.
Things seem to be better now. However, you wouldn't like to use all this cumbersome structure in your main code. It is better to write a promise or some class helper that will undertake all operations with threads.
Now you can easily use this snippet in your main code with simple async/await
or then/catch
. The final implementation of the algorithm for file processing with the help of threads will look like this.
You can see the full code here.
To implement this algorithm with the help of Worker Threads class, keep in mind that this class is experimental and it’s better not to use it in real projects. Ensure that your Node version is v10.8.0 and you run your code as node ----experimental-worker <you-js-file-path>
. The implementation of this algorithm is similar to the previous one, but slightly easier.
First, create ./src/utils/node.v10.8.0-fft-thread-worker.jsjs
file with the code that will be run in the child thread.
Everything is quite simple here. Import workerData
transferred from the main thread and parentPort
to send the result to this thread. There is also an additional constant isMainThread
, whose name speaks for itself.
Now you can use this file in the class Worker Threads. Implementation of this algorithm with the help of Worker Threads will look like this:
As I've mentioned before, you can also write an additional class.
Summary
In this article, I have shown you the process of parallelizing tasks in Node.js. By using one of the described methods, you can write really complex programs. But keep in mind that it also takes a lot of time for the processor to create a separate thread. So if your task doesn't require significant СPU load, its parallelizing will only slow down your program. Thread is just a tool. So my advice is to use this tool rationally.
P.S. Interested in Node.js? We have some additional interesting content for you!
Building home automation open-source app with React Native, Node, Express, and Raspberry Pi
How to create home automation app for clap detection with Node.js and Raspberry Pi