Workers
| Offload CPU | |
| Worker sketch | |
| Post messages | |
| Share carefully |
Offload CPU
Node runs JavaScript on a single thread, so a heavy loop blocks every request until it ends. The classic demo is a slow Fibonacci function that freezes the whole server.
Workers move that computation to a separate thread with its own event loop. The main thread stays free to answer requests while the worker crunches numbers.
function fib(n: number): number {
if (n < 2) {
return n;
}
return fib(n - 1) + fib(n - 2);
}
const slow: number = fib(40);
console.log(slow);
Worker sketch
A worker runs a separate script file in a separate thread. The main thread creates it with new Worker and points at the script path.
Communication stays event based: the worker posts its result, and the main thread listens for a message event. Neither side shares variables, they only exchange messages.
import { Worker } from "node:worker_threads";
const worker = new Worker("./fib.js");
worker.on("message", function (value): void {
console.log(value);
});
Post messages
Both sides send data with postMessage and receive it through message listeners. Only clonable values travel across the boundary, which rules out functions and live sockets.
Add an error listener next to the message listener from day one. Worker crashes then surface as events instead of silent hangs.
// same worker as above
worker.postMessage(40);
worker.on("error", function (err): void {
console.log(err);
});
Share carefully
Workers do not share memory by default, so each side sees its own copy of the data. Treat every message as a snapshot and design the protocol around small explicit payloads.
Shared buffers exist but add real complexity around locking and ordering. Stay with plain messages until profiling proves that copying is the bottleneck.
import { isMainThread } from "node:worker_threads";
if (isMainThread) {
console.log("main thread plans work");
} else {
console.log("worker computes");
}
Next: Deserialization
Article author: Arthur Isaev