Node.js Worker Threads
Hanan Hamza
Posted on June 29, 2022
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)
}
})
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()
// 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}`));
});
});
};
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:
- Worker Threads Tutorial
- Github Repo from Video
- Live Demo Link from Video
- Log Rocket Worker Threads
- Use Cases of Worker Threads
- WorkerPool
Further Reading
Posted on June 29, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024