What is the difference between spawn and fork?
In Node.js, both `child_process.spawn()` and `child_process.fork()` are functions used to create new processes that run independently of the main Node.js process. While they both achieve process creation, they are designed for different use cases and have distinct underlying mechanisms.
Core Concepts
child_process.spawn(): This function is used to launch a new process with a given command and a list of arguments. It's the most fundamental child process function in Node.js. It streams data from the child process back to the parent. spawn is ideal for running external system commands like ls, grep, or even non-Node.js programs, and for operations that involve large amounts of data being streamed in or out.
child_process.fork(): This function is a special case of spawn(). It's specifically designed to spawn new Node.js processes. The key difference is that fork() establishes an Inter-Process Communication (IPC) channel between the parent and child processes. This channel allows messages to be passed back and forth, making it perfect for creating worker processes that perform tasks and report results back to the main application. Each forked process runs a separate V8 instance, ensuring isolation.
Key Differences Summarized
| Feature | `child_process.spawn()` | `child_process.fork()` |
|---|---|---|
| Purpose | Run any external command/program | Run new Node.js processes |
| IPC Channel | No built-in IPC (can be simulated with stdio) | Built-in IPC channel for message passing |
| Communication | Standard I/O streams (stdin, stdout, stderr) | Message-passing via `send()` and `on('message')` |
| V8 Instance | Not applicable (for non-Node.js programs); uses same V8 as parent if Node.js | Always a new, separate V8 instance |
| Use Cases | Executing shell commands, running non-Node.js scripts, streaming data | Creating worker processes, parallelizing CPU-bound tasks, managing long-running operations |
Example: Using `spawn`
This example demonstrates spawning a simple ls -lh command to list files in the /usr directory.
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
Example: Using `fork`
This example shows a parent process forking a child Node.js script and exchanging messages. Assume child.js contains a simple message handler.
// parent.js
const { fork } = require('child_process');
const path = require('path');
const child = fork(path.join(__dirname, 'child.js'));
child.on('message', (message) => {
console.log('Message from child:', message);
});
child.send({ hello: 'child' }); // Send message to child
child.on('exit', (code) => {
console.log(`Child process exited with code ${code}`);
});
// --- child.js (conceptual content for the child script) ---
// process.on('message', (message) => {
// console.log('Message from parent:', message);
// process.send({ hello: 'parent', result: 42 }); // Send message back
// });
// process.exit(0);
For child.js to work with the parent, it would typically listen for message events on the global process object and use process.send() to reply.