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

WorkloadTool
CPU-bound JS (parsing, crypto, image math)worker_threads
Scaling an HTTP server across corescluster or a process manager
Running another program / shell commandchild_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);

Transfer vs Clone vs Share

// Default: structured clone (copies the data)
worker.postMessage(bigObject);

// Transfer: zero-copy move — source becomes unusable
const buf = new ArrayBuffer(1024 * 1024);
worker.postMessage(buf, [buf]);       // buf.byteLength is now 0 here

// Share: both threads see the same memory
const shared = new SharedArrayBuffer(4);
const counter = new Int32Array(shared);
worker.postMessage(shared);           // no transfer list needed

// Coordinate shared memory with Atomics
Atomics.add(counter, 0, 1);
Atomics.load(counter, 0);
Atomics.wait(counter, 0, 0);          // block worker until notified
Atomics.notify(counter, 0);

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