Node.js Cheatsheet
Worker Threads
Use this Node.js reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
When to Use Worker Threads
| Workload | Tool |
|---|---|
| CPU-bound JS (parsing, crypto, image math) | worker_threads |
| Scaling an HTTP server across cores | cluster or a process manager |
| Running another program / shell command | child_process |
| I/O-bound work (network, disk) | Plain async — the event loop already scales it |
Workers run JS in parallel threads with isolated event loops and memory;
they communicate by message passing (structured clone) or SharedArrayBuffer.
Basic Worker
// main.js import { Worker } from "node:worker_threads"; const worker = new Worker(new URL("./worker.js", import.meta.url), { workerData: { start: 0, end: 1e9 }, // cloned into the worker }); worker.on("message", (result) => console.log("sum:", result)); worker.on("error", (err) => console.error(err)); worker.on("exit", (code) => { if (code !== 0) console.error(`worker exited with ${code}`); }); // worker.js import { parentPort, workerData } from "node:worker_threads"; let sum = 0; for (let i = workerData.start; i < workerData.end; i++) sum += i; parentPort.postMessage(sum);
Single-File Pattern
import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads"; import { fileURLToPath } from "node:url"; if (isMainThread) { const worker = new Worker(fileURLToPath(import.meta.url), { workerData: 42 }); worker.on("message", console.log); } else { parentPort.postMessage(heavyCompute(workerData)); }
Two-Way Messaging
// Main → worker and back worker.postMessage({ cmd: "process", payload }); worker.on("message", (msg) => { ... }); // Inside the worker parentPort.on("message", (msg) => { const result = handle(msg); parentPort.postMessage(result); }); // Dedicated channel between any two threads import { MessageChannel } from "node:worker_threads"; const { port1, port2 } = new MessageChannel(); worker.postMessage({ port: port2 }, [port2]); // transfer the port port1.on("message", console.log);
Worker Pool
// Workers are expensive (~ms to spawn) — reuse them for many tasks. // Simple round-robin pool: import { Worker } from "node:worker_threads"; import os from "node:os"; const size = os.availableParallelism(); const workers = Array.from({ length: size }, () => new Worker(new URL("./worker.js", import.meta.url))); let next = 0; function runTask(data) { const worker = workers[next++ % size]; return new Promise((resolve, reject) => { const onMsg = (result) => { cleanup(); resolve(result); }; const onErr = (err) => { cleanup(); reject(err); }; const cleanup = () => { worker.off("message", onMsg); worker.off("error", onErr); }; worker.once("message", onMsg); worker.once("error", onErr); worker.postMessage(data); }); }
For production pools use
piscina(npm) — queueing, backpressure, and per-task cancellation are already solved there.
Worker Options and Utilities
new Worker("./worker.js", { workerData: any, // cloned initial data env: { NODE_ENV: "test" }, // worker's process.env (default: shared) execArgv: ["--max-old-space-size=512"], resourceLimits: { maxOldGenerationSizeMb: 256 }, stdout: true, // pipe worker stdout to worker.stdout stream name: "image-resizer", // shows in debugger }); await worker.terminate(); // force-stop, resolves with exit code import { threadId } from "node:worker_threads"; threadId; // 0 in main thread, unique per worker