Node.js Child Process Deep Dive — Complete Guide to spawn, exec, and fork
In this tutorial, you will learn about Node.js Child Process Deep Dive. We cover key concepts, practical examples, and best practices to help you master this topic.
Node.js child process deep dive covers the four methods for creating subprocesses: spawn (streamed), exec (buffered), fork (IPC), and execFile (binary), with stdio and error handling.
What You'll Learn
By the end of this tutorial, you'll use spawn for streaming output, exec for buffered results, fork for inter-process communication, manage child process lifecycles, and handle errors gracefully.
Why Child Processes Matter
Child processes offload CPU-intensive work, run system commands, and enable multi-process architectures. They are essential for image processing, video encoding, and Shell Script integration.
Real-World Use
A thumbnail generator spawns ImageMagick processes for each image, limits concurrent processes to CPU count, collects results via stdout, and kills hanging processes after timeout.
Child Process Path
flowchart LR
A[Architecture] --> B[Child Process Deep]
B --> C[Worker Threads]
B --> D[Cluster Module]
B --> E[PM2]
B --> F{You Are Here}
style F fill:#f90,color:#fff
Spawning Processes
spawn streams output, uses less memory for large data, and is the preferred method for most cases.
const { spawn } = require("node:child_process");
const ls = spawn("ls", ["-la", "/tmp"]);
ls.stdout.on("data", (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on("data", (data) => {
console.error(`stderr: ${data}`);
});
ls.on("close", (code) => {
console.log(`Process exited with code ${code}`);
});
Exec for Buffered Output
exec buffers all output in memory. Use for short commands with small output.
const { exec } = require("node:child_process");
exec("du -sh /tmp", { maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
if (err) {
console.error("Error:", err.message);
return;
}
console.log("Disk usage:", stdout.trim());
});
Forked Processes with IPC
fork creates a Node.js subprocess with IPC channel for message passing.
// parent.js
const { fork } = require("node:child_process");
const child = fork("./worker.js");
child.send({ task: "compute", data: { iterations: 1000000 } });
child.on("message", (result) => {
console.log("Result from child:", result);
child.kill();
});
child.on("exit", (code) => {
console.log("Child exited with code:", code);
});
stdio Configuration
Control stdin, stdout, stderr streams with the stdio option.
const { spawn } = require("node:child_process");
const child = spawn("wc", ["-w"], {
stdio: ["pipe", process.stdout, "pipe"],
});
child.stdin.write("Hello world from child process");
child.stdin.end();
child.on("close", (code) => {
console.log("\nDone with code:", code);
});
Process Pools
Manage a pool of child processes for concurrent task execution.
class ProcessPool {
constructor(maxProcesses = require("node:os").cpus().length) {
this.max = maxProcesses;
this.active = 0;
this.queue = [];
}
spawn(modulePath, args = []) {
return new Promise((resolve, reject) => {
const run = () => {
this.active++;
const child = require("node:child_process").fork(modulePath);
child.send({ args });
child.on("message", (result) => {
this.active--;
this.processQueue();
resolve(result);
});
child.on("error", (err) => {
this.active--;
this.processQueue();
reject(err);
});
};
if (this.active >= this.max) {
this.queue.push(run);
} else {
run();
}
});
}
processQueue() {
if (this.queue.length > 0 && this.active < this.max) {
const next = this.queue.shift();
next();
}
}
}
Common Mistakes
1. Not Handling Large stdout with exec
exec buffers all output. For large output, use spawn with streaming.
2. Ignoring Process Exit Codes
A non-zero exit code indicates failure. Always check exit codes.
3. Leaving Zombie Processes
Unattached child processes become zombies. Ensure proper cleanup on parent exit.
4. Security Issues with exec
exec runs commands through a shell. Shell metacharacters in arguments cause Command Injection. Use spawn with args array.
5. Not Setting Timeouts
Hanging child processes never terminate. Set timeout options or kill after a deadline.
Practice Questions
1. When should you use spawn vs exec?
spawn for streaming large output and binary data. exec for short commands with small output needing the result in a callback.
2. How does fork differ from spawn?
fork creates a Node.js process with IPC channel. spawn runs any executable.
3. What is the risk of command injection with exec?
exec runs commands through a shell. User input with semicolons or pipes executes additional commands.
4. How do you kill a hanging child process?
Call child.kill(signal) or set killSignal option. SIGTERM is default.
5. Challenge: Create a process pool that limits concurrent child processes.
class LimitedPool {
constructor(limit) {
this.limit = limit;
this.active = 0;
this.queue = [];
}
exec(cmd, args) {
return new Promise((resolve, reject) => {
const run = () => {
this.active++;
const proc = require("child_process").spawn(cmd, args);
let out = "";
proc.stdout.on("data", (d) => out += d);
proc.on("close", (code) => { this.active--; this.next(); resolve(out); });
proc.on("error", reject);
};
this.active < this.limit ? run() : this.queue.push(run);
});
}
next() { if (this.queue.length && this.active < this.limit) this.queue.shift()(); }
}
FAQ
Mini Project: Command Runner with Timeout
Build a safe command execution utility with timeout and output limits.
const { spawn } = require("node:child_process");
class SafeCommandRunner {
run(command, args = [], options = {}) {
const timeout = options.timeout || 30000;
const maxOutput = options.maxOutput || 1048576;
return new Promise((resolve, reject) => {
const child = spawn(command, args, { timeout });
let stdout = "";
let stderr = "";
child.stdout.on("data", (d) => { stdout += d; if (stdout.length > maxOutput) child.kill(); });
child.stderr.on("data", (d) => { stderr += d; });
child.on("close", (code) => resolve({ code, stdout, stderr, timedOut: false }));
child.on("error", reject);
child.on("timeout", () => { child.kill(); resolve({ code: null, stdout, stderr, timedOut: true }); });
});
}
}
What's Next
Node.js Cluster Module Node.js Worker Threads Node.js PM2
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro