Boost Node.js Performance with Worker Threads
Node.js is great for handling multiple requests asynchronously, but what if I tell you it struggles with CPU-intensive tasks? You may have noticed the server slows down when processing large files or running complex calculations. As we know, Node.js runs on a single thread, meaning CPU-heavy tasks like large calculations or image processing can block the server. The async/await can’t help because JS’s event loop is still single-threaded. Well, the solution for this problem is “Worker Threads!” Worker Threads help us run tasks in separate threads without blocking the main event loop. We will understand this by a simple example of a CPU-intensive calculation with and without Worker Threads. So, first we’ll create a function to calculate the “perfect numbers” below a certain number. function isPerfectNumber(num) { // Find all divisors and sum them let sum = 0; for(let i = 1; i < num; i++) { if(num % i === 0) { sum += i; } } // If sum of divisors equals the number, it's perfect return sum === num; } function findPerfectNumbers(maxNumber) { const perfectNumbers = []; // Check each number up to maxNumber for(let i = 1; i { console.log("Worker Result:", result); }); console.log("Main thread continues executing..."); // Add a simple counting task to show blocking console.log("\nStarting simple counting task..."); console.time("Simple Count"); let count = 0; for(let i = 0; i < 10; i++) { console.log(`Counting: ${i}`); count++; // Small delay to make the counting visible const start = Date.now(); while(Date.now() - start < 1000) {} // Busy wait for 1 second } console.timeEnd("Simple Count"); From the main file, we basically define the worker first, by giving the path of the worker file, and then post a “message” event to the worker. In this case, we are sending the number “1,00,000”. And then we’ll listen for the “message” event from the worker by using the “on” method. After this, we have used the same logic to log the count. The result of this is as expected. After the worker is called for the calculation, the main thread is not blocked and starts executing the next count script immediately. And when the result of the Worker Thread is ready, it’ll send it back through the “message” event. We can use this in case of CPU-intensive tasks. Better not to use these for asynchronous tasks like database queries. Github- https://github.com/sumankalia/react_web_workers/tree/type If you want to watch this tutorial on YouTube in Hindi. Here's the link If you like the above article, please clap on the article. Thanks for reading.

Node.js is great for handling multiple requests asynchronously, but what if I tell you it struggles with CPU-intensive tasks?
You may have noticed the server slows down when processing large files or running complex calculations.
As we know, Node.js runs on a single thread, meaning CPU-heavy tasks like large calculations or image processing can block the server.
The async/await can’t help because JS’s event loop is still single-threaded.
Well, the solution for this problem is “Worker Threads!”
Worker Threads help us run tasks in separate threads without blocking the main event loop.
We will understand this by a simple example of a CPU-intensive calculation with and without Worker Threads.
So, first we’ll create a function to calculate the “perfect numbers” below a certain number.
function isPerfectNumber(num) {
// Find all divisors and sum them
let sum = 0;
for(let i = 1; i < num; i++) {
if(num % i === 0) {
sum += i;
}
}
// If sum of divisors equals the number, it's perfect
return sum === num;
}
function findPerfectNumbers(maxNumber) {
const perfectNumbers = [];
// Check each number up to maxNumber
for(let i = 1; i <= maxNumber; i++) {
if(isPerfectNumber(i)) {
perfectNumbers.push(i);
}
}
return perfectNumbers;
}
module.exports = {
isPerfectNumber,
findPerfectNumbers
};
Now, we will use this to check if it's blocking the next process while execution. We’ll also add a function that will log the count after executing the “Main thread finished” log.
console.log("Main thread started");
console.time("Without Worker");
console.log("Result:", findPerfectNumbers(100000));
console.timeEnd("Without Worker");
console.log("Main thread finished");
// Add a simple counting task to show blocking
console.log("\nStarting simple counting task...");
console.time("Simple Count");
let count = 0;
for(let i = 0; i < 10; i++) {
console.log(`Counting: ${i}`);
count++;
// Small delay to make the counting visible
const start = Date.now();
while(Date.now() - start < 1000) {} // Busy wait for 1 second
}
console.timeEnd("Simple Count");
In the above example, when we run it, we’ll notice that when the execution of “findPerfectNumbers” starts, it blocks the other execution for approximately. 7 seconds(in my Macbook Air). After its execution, the counter starts executing.
Now, we have understood the problem, that we can’t block the server for any process for this long time. The server must be responsive. So, for these types of heavy calculations, we use Worker Threads.
Let’s solve this problem with the Worker Thread.
“worker_threads” is a core module of Node.js; we don’t have to install it separately.
Let’s create a separate file as “worker.js”
const { parentPort } = require('worker_threads');
const { findPerfectNumbers } = require('./helpers');
//Listen for messages from the main thread
parentPort.on('message', (num) => {
const result = findPerfectNumbers(num);
parentPort.postMessage(result); //Send result back
})
In this file, we’ll write the worker logic. Basically, it will listen for the “message” from parentPort or from the main thread where it's called. When it receives this event, it starts executing and returns the result to the main thread after execution.
Now, we will call this worker from our main file,
const { Worker } = require('worker_threads');
// Create a worker thread
const worker = new Worker('./worker.js');
worker.postMessage(100000); // Send a number to the worker for calculation
worker.on('message', (result) => {
console.log("Worker Result:", result);
});
console.log("Main thread continues executing...");
// Add a simple counting task to show blocking
console.log("\nStarting simple counting task...");
console.time("Simple Count");
let count = 0;
for(let i = 0; i < 10; i++) {
console.log(`Counting: ${i}`);
count++;
// Small delay to make the counting visible
const start = Date.now();
while(Date.now() - start < 1000) {} // Busy wait for 1 second
}
console.timeEnd("Simple Count");
From the main file, we basically define the worker first, by giving the path of the worker file, and then post a “message” event to the worker. In this case, we are sending the number “1,00,000”. And then we’ll listen for the “message” event from the worker by using the “on” method. After this, we have used the same logic to log the count.
The result of this is as expected. After the worker is called for the calculation, the main thread is not blocked and starts executing the next count script immediately. And when the result of the Worker Thread is ready, it’ll send it back through the “message” event.
We can use this in case of CPU-intensive tasks. Better not to use these for asynchronous tasks like database queries.
Github- https://github.com/sumankalia/react_web_workers/tree/type
If you want to watch this tutorial on YouTube in Hindi. Here's the link
If you like the above article, please clap on the article.
Thanks for reading.