DEV Community

Hanan Hamza
Hanan Hamza

Posted on

Node.js Worker Threads

The What

node:worker_threads module enables the use of threads that execute JavaScript in parallel.Unlike child_process or cluster, worker_threads can share memory.

The Why

Workers (threads) are useful for performing CPU-intensive JavaScript operations. They are lightweight and cheap as compared to other resources (child processes, cluster module).
Imagine that a calculation takes 10 seconds. If we are running a web server, that means that all of the other requests get blocked for at least 10s because of that calculation. That’s a disaster; anything more than 100ms could be too much.

let's take an example, imagine we now have a query that returns a few thousand results and we need to decrypt the values in our JavaScript code:

db.findAll('SELECT ...', function(err, results) {
  if (err) return console.error(err)


  // Heavy computation and many results
  for (const encrypted of results) {
    const plainText = decrypt(encrypted)
    console.log(plainText)
  }
})
Enter fullscreen mode Exit fullscreen mode

We will get the results in the callback once they are available. Then, no other JavaScript code is executed until our callback finishes its execution.

Why Worker Threads over Child Processes

The reality is that we can already do background processing in Node.js: we can fork the process and do exactly that using message passing, which you can imagine as simply as passing a message from one process to another. Well, hold on. This is a solution, but it’s not the ideal solution. Forking a process is expensive and slow — it means running a new virtual machine from scratch and using a lot of memory, since processes don’t share memory.

The How

Let’s say you are building an application that allows users to upload a profile image and then you generate multiple sizes (eg: 100 x 100 and 64 x 64) of the image for the various use cases within the application. The process of resizing the image is CPU intensive and having to resize into two different sizes would also increase the time spent by the CPU resizing the image. The task of resizing the image can be outsourced to a separate thread while the main thread handles other lightweight tasks.

// worker.js
const { parentPort, workerData } =  require("worker_threads");
const  sharp  =  require("sharp");

async  function  resize() {

    const  outputPath  =  "public/images/" + Date.now() +  ".png";
    const { image, size } =  workerData;

    await  sharp(image)
    .resize(size, size, { fit:  "cover" })
    .toFile(outputPath);
 parentPort.postMessage(outputPath);
}
resize()
Enter fullscreen mode Exit fullscreen mode
// mainThread.js
const { Worker } =  require("worker_threads");

module.exports  =  function  imageResizer(image, size) {

    return  new  Promise((resolve, reject) => {
    const  worker  =  new  Worker(__dirname  +    "/worker.js", {
workerData: { image, size }
});
    worker.on("message", resolve);
    worker.on("error", reject);
    worker.on("exit", code  => {
        if (code  !==  0)
            reject(new  Error(`Worker stopped with exit code ${code}`));
        });
    });
};
Enter fullscreen mode Exit fullscreen mode

What is the Web Workers API?

Maybe you’ve heard of the Web Workers API. The API is different from worker_threads because the needs and technical conditions are different, but they can solve similar problems in the browser runtime.

The Web Workers API is more mature and is well supported by modern browsers. It can be useful if you are doing crypto mining, compressing/decompressing, image manipulation, computer vision (e.g., face recognition), etc. in your web application.

Contrived Example

Six Degrees of Kevin Bacon. In this example, you'd see how by using worker threads running time was reduced from 14 seconds to half a second

Reference:

Further Reading

Top comments (0)