In Node.js, the child_process module enables you to create new processes from within your Node.js application. This can be useful for various tasks like executing shell commands, spawning new applications, and handling computationally intensive tasks in parallel to improve performance. This chapter will guide you through the basic concepts to advanced usage of child processes in Node.js.
A child process is a process created by another process (the parent process). In Node.js, this parent process is typically your main application. Child processes can run independently of the parent process and can be used to perform tasks concurrently.
Node.js provides three main methods to create child processes:
exec
: Executes a command in a shell and buffers the output.spawn
: Launches a new process with a given command and arguments.fork
: Special case of spawn
used to create new Node.js processes.The exec
method is used to run shell commands and buffer the output. It’s straightforward to use when you need to run a command and get the result back.
const { exec } = require('child_process');
exec('ls -l', (error, stdout, stderr) => {
if (error) {
console.error(`Error: ${error.message}`);
return;
}
if (stderr) {
console.error(`Stderr: ${stderr}`);
return;
}
console.log(`Output: ${stdout}`);
});
exec('ls -l', callback)
: Runs the ls -l
command.callback(error, stdout, stderr)
: Handles the command’s output and errors.error
: Contains error details if the command fails.stdout
: Standard output of the command.stderr
: Standard error of the command.
Output: total 0
-rw-r--r-- 1 user staff 0 Jul 4 10:00 file1.txt
-rw-r--r-- 1 user staff 0 Jul 4 10:00 file2.txt
Handling errors is crucial when working with child processes. The exec
method’s callback provides the error
and stderr
parameters to manage this.
Example:
const { exec } = require('child_process');
exec('invalid-command', (error, stdout, stderr) => {
if (error) {
console.error(`Execution error: ${error.message}`);
return;
}
if (stderr) {
console.error(`Command error: ${stderr}`);
return;
}
console.log(`Output: ${stdout}`);
});
Execution error: Command failed: invalid-command
/bin/sh: invalid-command: command not found
You can pass options to exec
to control the environment, working directory, and more.
Example:
const { exec } = require('child_process');
exec('ls -l', { cwd: '/path/to/directory' }, (error, stdout, stderr) => {
if (error) {
console.error(`Error: ${error.message}`);
return;
}
if (stderr) {
console.error(`Stderr: ${stderr}`);
return;
}
console.log(`Output: ${stdout}`);
});
The spawn
method is used for more complex scenarios where you need to handle the data streams of the child process.
Example:
const { spawn } = require('child_process');
const ls = spawn('ls', ['-l']);
ls.stdout.on('data', (data) => {
console.log(`Output: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`Error: ${data}`);
});
ls.on('close', (code) => {
console.log(`Child process exited with code ${code}`);
});
spawn('ls', ['-l'])
: Spawns a new process running ls -l
.stdout.on('data')
: Listens for data on the standard output.stderr.on('data')
: Listens for data on the standard error.on('close')
: Executes when the process exits.
Output: total 0
-rw-r--r-- 1 user staff 0 Jul 4 10:00 file1.txt
-rw-r--r-- 1 user staff 0 Jul 4 10:00 file2.txt
Child process exited with code 0
One of the key advantages of spawn
is its ability to handle streaming data efficiently.
const { spawn } = require('child_process');
const tail = spawn('tail', ['-f', '/var/log/system.log']);
tail.stdout.on('data', (data) => {
console.log(`Log: ${data}`);
});
spawn('tail', ['-f', '/var/log/system.log'])
: Spawns a process that continuously outputs new log entries.
// Output
Log: Jul 4 10:00:00 user system: System started
Log: Jul 4 10:00:05 user system: System running
You can control the environment, custom IO streams, and more with spawn
.
const { spawn } = require('child_process');
const ls = spawn('ls', ['-l'], {
cwd: '/path/to/directory',
env: { ...process.env, CUSTOM_VAR: 'value' }
});
ls.stdout.on('data', (data) => {
console.log(`Output: ${data}`);
});
The fork
method is specifically designed to spawn new Node.js processes. It enables communication between parent and child processes using IPC.
Example:
const { fork } = require('child_process');
const child = fork('child.js');
child.on('message', (message) => {
console.log(`Received message from child: ${message}`);
});
child.send('Hello from parent');
fork('child.js')
: Forks a new Node.js process running child.js
.on('message')
: Listens for messages from the child process.send(message)
: Sends a message to the child process.
// Output (Parent Process)
Received message from child: Hello from child
// Output (Child Process):
process.on('message', (message) => {
console.log(`Received message from parent: ${message}`);
process.send('Hello from child');
});
IPC allows parent and child processes to exchange messages, making it easier to manage tasks.
const { fork } = require('child_process');
const child = fork('child.js');
child.on('message', (message) => {
console.log(`Received message from child: ${message}`);
});
child.send({ task: 'processData', data: [1, 2, 3] });
fork('child.js')
: Forks a new Node.js process running child.js
.on('message')
: Listens for messages from the child process.send(message)
: Sends a message to the child process.
// Output (Parent Process)
Received message from child: { result: [2, 4, 6] }
// Output (Child Process):
process.on('message', (message) => {
if (message.task === 'processData') {
const result = message.data.map(x => x * 2);
process.send({ result });
}
});
You can handle complex tasks and multiple messages with fork
.
Example:
const { fork } = require('child_process');
const child = fork('child.js');
child.on('message', (message) => {
console.log(`Received message from child: ${message}`);
});
child.send({ task: 'compute', data: 42 });
// Output (Parent Process)
Received message from child: { result: 1764 }
// Output (Child Process):
process.on('message', (message) => {
if (message.task === 'compute') {
const result = message.data * message.data;
process.send({ result });
}
});
spawn
for long-running processes or when you need to stream data.exec
for short, simple commands.fork
for Node.js specific tasks and when you need IPC.Child processes in Node.js provide a powerful way to handle tasks concurrently, improving performance and efficiency. By using exec, spawn, and fork, you can manage various types of processes, handle streaming data, and facilitate communication between parent and child processes. Understanding and using these tools effectively can greatly enhance your Node.js applications.Happy coding !❤️