How to Handle CPU Intensive Loads In Node JS ?
Last Updated :
24 Apr, 2025
Node is a Single-Threaded Non-blocking event-driven architecture. But when it comes to handling heavy CPU-intensive workloads it can face some challenges while handling it. In this article, we will explore strategies to tackle CPU-intensive loads in Node.js effectively, to handle demanding tasks like a hero.
What are CPU extensive workloads?
Regarding I/O intensive operations like calling external APIs, and fetching data from a database NodeJs seamlessly offloads the tasks to libuv to unblock the main thread and continue accepting further responses. However, when it comes to handling CPU-intensive tasks, the single-threaded nature of Node.js might block the main thread from taking any more requests to process. This is where the concept of multithreading comes to the rescue, enabling you to efficiently manage compute-heavy workloads.
When there is a non-blocking I/O operation like calling a third-party API or fetching something from the database, Node JS is smart enough to offload these tasks to worker threads in Libuv but there might be cases when you want to do some CPU-intensive tasks in such a case NodeJs won't automatically offload the task to libuv, it will continue using the main thread for the complete processing
Example: Here is a small nodejs server using expresjs which has exposed two different APIs. Here the blocking API which runs a simple for loop for 2000000000000 and sends a successful response.
Node
const express = require("express");
const app = express();
app.get("/blocking", (req, res) => {
const workerPromises = [];
for (let i = 0; i < 2000000000000; i++) { }
res.status(200).send('Processing completed');
});
app.get("/non-blocking", (req, res) => {
res.status(200).send('Non-blocking process completed');
});
app.listen(3000, () => { //listening to port 3000
console.log("Server listening on port 3000");
});
Try starting this server and hit the /blocking api from your browser and you will see the tab in continuously loading. Now, try hitting the /non-blocking api, you will see that this is also loading and not returning any response. This is because the main thread is blocked by the /blocking api.
To solve this we need to explicitly offload the task from the main thread to the Libuv worker threads so that our main thread can continue accepting other requests.
Example: Here non-blocking code sends a successful response.
Node
//index.js
const express = require("express");
const app = express();
const { Worker } = require('worker_threads');
app.get("/blocking", (req, res) => {
const worker = new Worker("./worker.js");
//receiving data from worker threads
worker.on("message", (message) => {
res.status(200).send(message);
});
//fired if an error occurs in the child thread
worker.on("error", (error) => {
res.status(500).send('Blocking process failed to complete');
});
});
app.get("/non-blocking", (req, res) => {
res.status(200).send('Non-blocking process completed');
});
app.listen(3000, () => {
console.log("Server listening on port 3000");
});
Node
//worker.js
const { parentPort, workerData } = require("worker_threads");
for (let i = 0; i < 2000000000000; i++)
parentPort.postMessage('Blocking operation successfully completed');
In the above code as you can see we have created a new worker.js file which contains the long CPU intensive task to be performed and when complete processing is done, the worker is communicating back to the main thread by posting a message. In the main index.js file we have created an instance of the worker task of worker.js file and it's listening to any message that the worker thread is posting.
Now, let's further optimise the code by sending some data from parent to worker thread this time. As you can see in the above code we are only spawning the task to one worker thread but in most of the modern computers we have more than 2 cores. Lets make use of that and try spawning 4 worker thread and see the difference.
Index.js
Node
//index.js
const express = require("express");
const app = express();
const { Worker } = require('worker_threads');
const THREADCOUNT = 4; //specifying the no of threads to spawn
function createWorker() {
return new Promise((resolve, reject) => {
const worker = new Worker("./worker.js", {
workerData: { threadCount: THREADCOUNT }, //sending data to worker threads
});
worker.on("message", (message) => {
resolve(message); //listening to messages from worker threads
});
worker.on("error", (error) => {
reject(error);
});
});
}
app.get("/blocking", (req, res) => {
const workerPromises = [];
for (let i = 0; i < THREADCOUNT; i++) {
workerPromises.push(createWorker());
}
const threadResults = await Promise.all(workerPromises);
const total = threadResults.reduce((a, b) => a + b, 0);
res.status(200).send(`Total: ${total}`);
});
app.get("/non-blocking", (req, res) => {
res.status(200).send('Non-blocking process completed');
});
app.listen(3000, () => {
console.log("Server listening on port 3000");
});
Node
//worker.js
const { parentPort, workerData } = require("worker_threads");
for (let i = 0; i < 2000000000000 / workerData.threadCount; i++) {} //using received data from main thread
parentPort.postMessage(1); //sending data to main thread
Output:In the above example we have spawned 4 worker threads and divided the task among them equally to optimise the performance. Here is the time taken by the /blocing API.
One Worker Thread
Four Worker Threads Explanation: You can clearly see the different between the above examples. Time taken by the first program which has only one worker thread is ~ 18 sec and the time taken by the server having 4 worker threads is ~ 4 sec.
Similar Reads
How to handle concurrency in Node.js ?
Node.js is an open-source, cross-platform runtime environment built on Chrome's V8 Engine. It is used to develop highly scalable backend as well as server-side applications. Node.js uses a single-threaded event loop architecture. It is also asynchronous in nature. These are the two main reasons why
4 min read
How to handle Child Threads in Node.js ?
Node.js is a single-threaded language and uses the multiple threads in the background for certain tasks as I/O calls but it does not expose child threads to the developer.But node.js gives us ways to work around if we really need to do some work parallelly to our main single thread process.Child Pro
2 min read
How to integrate Express-rate-limit in Node.js ?
Rate limiting prevents the same IP from making too many requests that will help us prevent attacks like brute force. The express-rate-limit is the npm package for limiting the request from the user. Project Setup: Run the following sets of commands to create a folder and initialize the project. mkd
2 min read
If Node.js is single threaded then how to handles concurrency ?
Node js is an open-source virtual machine that uses javascript as its scripting language. Despite being single-threaded, it is one of the most popular web technologies. The reason why node js is popular despite being single-threaded is the asynchronous nature that makes it possible to handle concurr
4 min read
How to handle streaming data in Node ?
Streaming data in NodeJS involves processing data in chunks as it becomes available, rather than waiting for the entire dataset to be loaded into memory. This approach is particularly useful for handling large files, network data, or real-time data sources. NodeJS provides a powerful set of streamin
2 min read
How to Run Cron Jobs in Node.js ?
Cron jobs are scheduled tasks that run at specific intervals in the background, commonly used for maintenance or repetitive tasks. Users can schedule commands the OS will run these commands automatically according to the given time. It is usually used for system admin jobs such as backups, logging,
4 min read
How to Run C Code in NodeJS?
Developers can take advantage of Node.js's robust ecosystem and performance by running C code within the framework. Child processes, Node.js extensions, and the Foreign Function Interface (FFI) can all assist in this. There is flexibility to integrate C code based on particular requirements, as each
3 min read
How to handle asynchronous operations in Node ?
NodeJS, renowned for its asynchronous and event-driven architecture, offers powerful mechanisms for handling asynchronous operations efficiently. Understanding how to manage asynchronous operations is crucial for NodeJS developers to build responsive and scalable applications. What are Asynchronous
2 min read
How to Take Input in Node.js ?
Taking input in a Node.js application is essential for building interactive command-line interfaces, processing user input, and creating dynamic applications. Node.js provides several methods for receiving input from users, including reading from standard input (stdin), command-line arguments, and u
2 min read
How to Access Cache Data in Node.js ?
Caching is an essential technique in software development that helps improve application performance and scalability by temporarily storing frequently accessed data. In Node.js, caching can significantly reduce database load, minimize API response times, and optimize resource utilization. This artic
5 min read