Worker_threads in Nodejs

Worker_threads in Nodejs

Utilize worker_threads to perform CPU-intensive task in Nodejs

Table of contents

No heading

No headings in the article.

Worker_threads is a built-in module in Nodejs that lets you run multiple threads in parallel which executes the Javascript, so now the question arises, when do we need to run multiple threads? well, when you want to run a CPU-intensive task in parallel to the main JavaScript thread without blocking the event loop. and we can also take advantage of multiple CPU cores and increase the performance of our app.

So, now we are going to understand with example.

I am going to create one HTTP server which will listen on port 8080.

const http = require('http')

const server = http.createServer().listen(8080, () =>{
    console.log('server started on port 8080');
})

I am going to create 2 routes.

1) '/ShowAllFiles'

This route returns all the file name that is available on the server and shows it to the user. (I have hardcoded file name here but you can imagine the big number here)

const http = require('http')

const server  = http.createServer().listen(8080, () =>{
    console.log('server started on port 8080')
})

var listOfFiles = ["first.txt","second.txt","third.txt","four.txt","five.txt"]

server.on('request', (req,res) => {
    if(req.url === "/ShowAllFiles"){
        res.end(JSON.stringify({
            files : listOfFiles
        }))
    }
})

2) '/ZipAllFiles'

This route creates the zip of all the files available on the server and returns it to the client. (I am not actually going to do that we are going to pretend that this is a long CPU-intensive task as this article is not related to compression)

const http = require('http')

const server  = http.createServer().listen(8080, () =>{
    console.log('server started on port 8080');
})

var listOfFiles = ["first.txt","second.txt","third.txt","four.txt","five.txt"]

server.on('request', (req,res) => {
    if(req.url === "/ShowAllFiles"){
        res.end(JSON.stringify({
            files : listOfFiles
        }))
    }
    else if(req.url === "/ZipAllFiles"){
        var isZipReady = zipIt()
        res.end(JSON.stringify({
            zipReadyToDownload : isZipReady
        }))
    }
})

const zipIt = () => {
    // we can actually have compression algorithm here but
    // here I am going to do CPU intensive work with loop
    for(var i=0;i<1000000000000;i++){
        let a = "zipping is in progress"    
    }
    return true
}

Now Run your node app, and visit the "/ShowAllFiles" route it will return JSON with all the files name

Now visit the second route which is "/ZipAllFiles" and we can see that due to CPU-intensive work, the Main thread got busy serving the request that it can not serve another request. after visiting this route visit the first route "/ShowAllFiles" and the Node App will not be able to serve this request because the main thread is blocked in CPU-intensive task.

To Solve this Issue we can use worker_threads module to do the CPU-intensive work.

So we need to create a file which will do all the CPU-intensive work so I am going to name the file HandleCompression.js and here is the code which we are going to put into it.

const { parentPort } = require('worker_threads')

for(var i=0;i<1000000000000;i++){
    let a = "zipping is in progress"    
}

parentPort.postMessage(true)
// using this method we can send data to the main thread
// from which this current thread is created
// so in out case true will be accessible to the main thread

Now from main thread we are going to create the worker thread whenever we want to do the CPU-intensive task so to do that we need to require the worker_threads node module and by passing the file name into the constructor we will get the worker thread running.

const http = require('http')
const {Worker} = require('worker_threads')

const server  = http.createServer().listen(8080, () =>{
    console.log('server started on port 8080');
})

var listOfFiles = ["first.txt","second.txt","third.txt","four.txt","five.txt"]

server.on('request', (req,res) => {
    if(req.url === "/ShowAllFiles"){
        res.end(JSON.stringify({
            files : listOfFiles
        }))
    }
    else if(req.url === "/ZipAllFiles"){
        // We are creating the worker thread here
        var compressionWorker = new Worker('./HandleCompression.js')
        // on that thread we can listen to the message event to get the data     
        // from worker thread
        compressionWorker.on('message', (msg) => {
            res.end(JSON.stringify({
                zipReadyToDownload : msg
            }))
        })
    }
})

Now run your node app and visit the "/ZipAllFiles" and "/ShowAllFiles" then we will see the difference in that the Node app will be able to serve the request without blocking because the CPU-intensive task has been assigned to the worker_threads and it will not block the main thread.

This is how we can easily avoid blocking the main thread for any CPU-intensive task and increase the performance of our app.