Last month when I was building an open-sourced project, I met with a situation that needs to run npm test
in the child process and send back the data to the main process,
So I did some investigation and figured out my solution in the end. Here is my summary of how many ways we can use for establishing nodejs process communication.
child_process.fork
fork gives you a communication channel by native, specify a file for the child process and they will establish a communication channel after generating the child process, as simple as what is shown in the Nodejs official document.
// index.js
import { fork } from 'node:child_process'
import path from 'path'
const subProcess = fork(path.resolve(process.cwd(),'sub.js'))
subProcess.on('message', (msg) => {
console.log('PARENT got message: ', msg)
subProcess.send('parent -> child')
})
// sub.js
process.send("child -> parent")
process.on('message', (msg) => {
console.log('CHILD got message: ', msg)
})
// output
PARENT got message: child -> parent
CHILD got message: parent -> child
child_process.spawn
fork works only if we have the file running in the child process. In some other cases, we might wanna use the child process to execute a command and we use spawn for such cases. I thought the spawned child process can't establish a communication channel until I found the parameters stdio, here is the example:
// index.js
import { spawn } from 'node:child_process'
const subProcess = spawn('node', ['sub.js'], {
// inherit stdin, stdout and stderr, establish IPC communication channel
stdio: ['inherit', 'inherit', 'inherit', 'ipc']
})
subProcess.on('message', (msg) => {
console.log('PARENT got message: ', msg)
subProcess.send('parent -> child')
});
// sub.js
process.send("child -> parent")
process.on('message', (msg) => {
console.log('CHILD got message: ', msg)
})
// output
PARENT got message: child -> parent
CHILD got message: parent -> child
Listen stdout or stderr
but for my case, I need my child process to run an npm script, somehow it can't establish the communication channel.
And finally, I have to switch to my tricky solution which passes the data through stdout or stderr, because I found the parent process can listen the data from child process's stdout and stderr if we set stdio as pipe, then what we need to do is just let the child process to output the data with a pre-defined format so the parent process can recognize the data by the format.
// index.js
const subProcess = spawn('node', 'run test', {
// pipe stderr so we can subsribe the data event on stderr
stdio: ['inherit', 'inherit', 'pipe']
});
subProcess.stderr.on('data', (buffer) => {
const data = buffer.toString()
if (data.startsWith('PREFIX')) {
// Here we know this is the data sent from the child process manually
}
})
// testing files
process.stderr.write('PREFIX' + 'message to parent' + "\n")
Conclusion
For establish a communication channel between node process:
- Using fork if you have the file for the child process
- Using spawn and modify stdio if you only need to run a command in the child process
- If you are running an NPM script, we can do communication through stdout and stderr.
Top comments (1)
thanks a lot, small mistake and a lot of work