ChildProcessclosures,退出事件之间的区别

当通过Node.js中的spawn()/exec()/...产生subprocess时,subprocess有一个'close''exit'事件。

这两者之间有什么区别,什么时候需要使用什么?

在Node.js 0.7.7之前,subprocess只有一个“退出”事件(没有“closures”事件)。 这个事件会在subprocess退出时触发,所有的stream(stdin,stdout,stdout)都被closures。

在节点0.7.7中 ,引入了“closures”事件( 参见提交 )。 文档(永久链接)目前说:

当closuressubprocess的stdiostream时,会发出'close'事件。 这与“退出”事件不同,因为多个进程可能共享相同的stdiostream。

如果你只是产生一个程序,并没有做任何特殊的事情,“closures”事件触发“退出”后。 如果将stdoutstream传送到另一个stream,则“closures”事件可能会延迟。 这意味着“closures”事件可能会在“退出”事件之后(无限期地)延迟。
这是否意味着“closures”事件总是在“退出”之后被解雇? 如下面的例子所示,答案是否定的。

所以,如果你只关心进程终止(例如,因为进程拥有独占资源),那么倾听“退出”就足够了。 如果您不关心程序,只关心其input和/或输出,请使用“closures”事件。

实验:杀害小孩之前销毁stdio

通过实验(在Node.js v7.2.0中),我发现如果stdiostream没有被subprocess使用,那么只有在程序退出后才会触发“close”事件:

 // The "sleep" command takes no input and gives no output. cp = require('child_process').spawn('sleep', ['100']); cp.on('exit', console.log.bind(console, 'exited')); cp.on('close', console.log.bind(console, 'closed')); cp.stdin.end(); cp.stdout.destroy(); cp.stderr.destroy(); console.log('Closed all stdio'); setTimeout(function() { console.log('Going to kill'); cp.kill(); }, 500); 

上面的程序产生“睡眠”输出:

 Closed all stdio Going to kill exited null SIGTERM closed null SIGTERM 

当我把第一行改成只输出的程序时,

 // The "yes" command continuously outputs lines with "y" cp = require('child_process').spawn('yes'); 

…那么输出是:

 Closed all stdio exited 1 null closed 1 null Going to kill 

同样,当我改变一个只能从stdin读取的程序时,

 // Keeps reading from stdin. cp = require('child_process').spawn('node', ['-e', 'process.stdin.resume()']); 

或者当我从标准input读取和输出到标准输出时,

 // "cat" without arguments reads from stdin, and outputs to stdout cp = require('child_process').spawn('cat'); 

实验:pipe程序到另一个,杀死第一个程序

以前的实验很人造。 下一个实验是更现实的一点:你把一个程序传给另一个,杀掉第一个。

 // Reads from stdin, output the input to stdout, repeat. cp = require('child_process').spawn('bash', ['-c', 'while read x ; do echo "$x" ; done']); cp.on('exit', console.log.bind(console, 'exited')); cp.on('close', console.log.bind(console, 'closed')); cpNext = require('child_process').spawn('cat'); cp.stdout.pipe(cpNext.stdin); setTimeout(function() { // Let's assume that it has started. Now kill it. cp.kill(); console.log('Called kill()'); }, 500); 

输出:

 Called kill() exited null SIGTERM closed null SIGTERM 

类似地,当第一个程序只从input读取而不输出时:

 // Keeps reading from stdin, never outputs. cp = require('child_process').spawn('bash', ['-c', 'while read ; do : ; done']); 

当第一个程序继续输出而不等待标准input时,行为是不同的,如下一个实验所示。

实验:pipe道程序有很多的输出到另一个,杀死第一个程序

 // Equivalent to "yes | cat". cp = require('child_process').spawn('yes'); cp.on('exit', console.log.bind(console, 'exited')); cp.on('close', console.log.bind(console, 'closed')); cpNext = require('child_process').spawn('cat'); cp.stdout.pipe(cpNext.stdin); setTimeout(function() { // Let's assume that it has started. Now kill it. cp.kill(); console.log('Called kill()'); setTimeout(function() { console.log('Expecting "exit" to have fired, and not "close"'); // cpNext.kill(); // ^ Triggers 'error' event, errno ECONNRESET. // ^ and does not fire the 'close' event! // cp.stdout.unpipe(cpNext.stdin); // ^ Does not appear to have any effect. // ^ calling cpNext.kill() throws ECONNRESET. // ^ and does not fire the 'close' event! cp.stdout.destroy(); // <-- triggers 'close' cpNext.stdin.destroy(); // ^ Without this, cpNext.kill() throws ECONNRESET. cpNext.kill(); }, 500); }, 500); 

上述程序输出如下,然后退出:

 Called kill() exited null SIGTERM Expecting "exit" to have fired, and not "close" closed null SIGTERM 

简短的版本是,'exit'在孩子退出但是stdio还没有closures的时候发出。 当孩子退出closures了他的工作室时,“closures”发出。

除此之外,他们共享相同的签名。

你看了文档吗?

据此:

当closuressubprocess的stdiostream时,会发出'close'事件。 这与“退出”事件不同,因为多个进程可能共享相同的stdiostream

subprocess结束后,“退出”事件发出。 如果进程退出,则代码是进程的最终退出代码,否则为空。 如果进程由于接收到信号而终止,则signal是该信号的string名称,否则为null。 其中一个将永远是非空的。