Child Processes & Worker Threads
📖 Concept
Node.js provides two mechanisms for parallel execution: Child Processes (separate OS processes) and Worker Threads (threads within the same process). Choose based on your use case.
Child Processes (child_process module):
Spawn separate OS processes to run system commands, scripts, or other programs.
| Method | Use Case |
|---|---|
exec(cmd) |
Run shell commands, get output as string (buffers stdout) |
execFile(file, args) |
Run a specific executable (faster, no shell) |
spawn(cmd, args) |
Stream-based for long-running processes |
fork(modulePath) |
Spawn a new Node.js process with IPC channel |
Worker Threads (worker_threads module):
Run JavaScript in parallel threads within the same process. Unlike child processes, workers share memory via SharedArrayBuffer.
| Feature | Child Process | Worker Thread |
|---|---|---|
| Isolation | Full (separate process) | Partial (same process) |
| Communication | IPC (serialized messages) | MessagePort + SharedArrayBuffer |
| Memory | Separate (duplicated) | Shared possible |
| Overhead | High (new V8 instance + OS process) | Lower (new V8 isolate) |
| Best for | System commands, CPU isolation | CPU-heavy JS computations |
When to use which:
- Child process: Running shell commands, Python scripts, system tools, process isolation for security
- Worker thread: CPU-intensive JavaScript (image processing, crypto, data parsing)
- Neither: I/O-bound work (use async I/O — the event loop handles it perfectly)
🏠 Real-world analogy: Child processes are like hiring a separate contractor — they work independently with their own tools. Worker threads are like assigning a colleague — they share office resources but work on different tasks simultaneously.
💻 Code Example
1// Child Processes & Worker Threads23const { exec, execFile, spawn, fork } = require("child_process");4const { Worker, isMainThread, parentPort, workerData } = require("worker_threads");5const { promisify } = require("util");6const execAsync = promisify(exec);78// === CHILD PROCESSES ===910// 1. exec — Run shell commands (output buffered)11async function runShellCommand() {12 try {13 const { stdout, stderr } = await execAsync("ls -la && echo 'Done!'");14 console.log("Output:", stdout);15 if (stderr) console.error("Stderr:", stderr);16 } catch (err) {17 console.error("Command failed:", err.message);18 }19}2021// 2. spawn — Stream-based (for long-running commands)22function runLongCommand() {23 const child = spawn("find", [".", "-name", "*.js", "-type", "f"]);2425 child.stdout.on("data", (data) => {26 console.log("Found:", data.toString().trim());27 });2829 child.stderr.on("data", (data) => {30 console.error("Error:", data.toString());31 });3233 child.on("close", (code) => {34 console.log(`Process exited with code ${code}`);35 });36}3738// 3. fork — Spawn Node.js process with IPC39// === parent.js ===40function forkWorker() {41 const worker = fork("./heavy-computation.js");4243 worker.send({ type: "start", data: { numbers: [1, 2, 3, 4, 5] } });4445 worker.on("message", (result) => {46 console.log("Result from worker:", result);47 });4849 worker.on("exit", (code) => {50 console.log(`Worker exited with code ${code}`);51 });52}5354// === heavy-computation.js ===55// process.on("message", (msg) => {56// if (msg.type === "start") {57// const result = msg.data.numbers.reduce((a, b) => a + b, 0);58// process.send({ type: "result", value: result });59// process.exit(0);60// }61// });6263// === WORKER THREADS ===6465// 4. Basic Worker Thread66if (isMainThread) {67 // Main thread68 function createWorker(data) {69 return new Promise((resolve, reject) => {70 const worker = new Worker(__filename, { workerData: data });7172 worker.on("message", resolve);73 worker.on("error", reject);74 worker.on("exit", (code) => {75 if (code !== 0) {76 reject(new Error(`Worker stopped with exit code ${code}`));77 }78 });79 });80 }8182 // Run CPU-intensive work in parallel83 async function parallelComputation() {84 const tasks = [85 { start: 0, end: 25000000 },86 { start: 25000000, end: 50000000 },87 { start: 50000000, end: 75000000 },88 { start: 75000000, end: 100000000 },89 ];9091 console.time("parallel");92 const results = await Promise.all(tasks.map(createWorker));93 const total = results.reduce((a, b) => a + b, 0);94 console.timeEnd("parallel");95 console.log("Sum:", total);96 }9798 // Compare with single-threaded99 async function singleThreaded() {100 console.time("single");101 let sum = 0;102 for (let i = 0; i < 100000000; i++) sum += i;103 console.timeEnd("single");104 console.log("Sum:", sum);105 }106} else {107 // Worker thread108 const { start, end } = workerData;109 let sum = 0;110 for (let i = start; i < end; i++) sum += i;111 parentPort.postMessage(sum);112}113114// 5. Worker pool pattern115class WorkerPool {116 constructor(workerScript, poolSize) {117 this.workerScript = workerScript;118 this.poolSize = poolSize;119 this.workers = [];120 this.queue = [];121 this.activeWorkers = 0;122 }123124 execute(data) {125 return new Promise((resolve, reject) => {126 this.queue.push({ data, resolve, reject });127 this.processQueue();128 });129 }130131 processQueue() {132 while (this.queue.length > 0 && this.activeWorkers < this.poolSize) {133 const { data, resolve, reject } = this.queue.shift();134 this.activeWorkers++;135136 const worker = new Worker(this.workerScript, { workerData: data });137138 worker.on("message", (result) => {139 this.activeWorkers--;140 resolve(result);141 this.processQueue();142 });143144 worker.on("error", (err) => {145 this.activeWorkers--;146 reject(err);147 this.processQueue();148 });149 }150 }151}152153// Usage:154// const pool = new WorkerPool("./worker.js", 4); // 4 worker threads155// const results = await Promise.all([156// pool.execute({ task: "process-image", path: "photo1.jpg" }),157// pool.execute({ task: "process-image", path: "photo2.jpg" }),158// pool.execute({ task: "process-image", path: "photo3.jpg" }),159// ]);
🏋️ Practice Exercise
Exercises:
- Use
execto run a system command and capture the output — handle both success and failure - Use
spawnto run a long-running command and stream its output in real-time - Create a forked Node.js process that performs heavy computation and sends results back via IPC
- Implement a Worker Thread that computes prime numbers in parallel across 4 threads
- Build a Worker Pool that reuses a fixed number of threads for queued tasks
- Compare the performance of single-threaded vs Worker Thread computation for a CPU-intensive task
⚠️ Common Mistakes
Using
exec()with user input without sanitization — this is a shell injection vulnerability; useexecFile()orspawn()with arguments array insteadUsing Worker Threads for I/O-bound tasks — the event loop already handles async I/O efficiently; Workers add overhead without benefit for I/O work
Not handling the 'error' event on child processes — errors in spawned processes crash silently if not caught
Creating too many Worker Threads — each thread has V8 overhead (~2-5MB); creating hundreds wastes memory; use a worker pool with a fixed size
Using
exec()for commands with large output — exec buffers ALL output in memory; usespawn()with streaming for large outputs
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Child Processes & Worker Threads. Login to unlock this feature.