在Node的Child Processspawn()
函数的文档中,以及在其他地方看到的示例中,模式是调用spawn()
函数,然后在返回的ChildProcess
对象上设置一堆处理程序。例如,这是该spawn()
文档页面上给出的第一个示例:
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}`);
});
该spawn()
函数本身在第二行上调用。我的理解是spawn()
异步启动子进程。从文档中:
child_process.spawn()方法使用给定命令在args中带有命令行参数来生成新进程。
但是,上面脚本的以下几行继续为该进程设置各种处理程序,因此假设该进程spawn()
在第2行被调用的时间之间并未真正启动(并可能完成),而其他事情发生在随后的行。我知道JavaScript / Node是单线程的。但是,操作系统不是单线程的,天真的会读到该spawn()
调用,告诉操作系统立即生成进程(此时,不幸的是,操作系统可能会挂起父Node进程并运行/完成。在执行下一行Node代码之前的子进程)。
但是必须确保在当前JavaScript函数完成之前(或更一般而言,调用当前函数的当前JavaScript事件处理程序完成),实际上不会产生该过程,对吗?
这似乎是一件非常重要的事情。为什么在“子进程”文档页面中没有这么说?是否有一些压倒一切的Node原理,使得不必明确地说出来?
新进程的产生立即开始(已移交给操作系统以实际启动该进程并使之继续运行)。使用.spawn()
异步和非阻塞启动新进程。因此,它将启动操作系统的操作并立即返回。您可能认为这就是为什么可以在事件返回后设置事件处理程序的原因(因为该过程尚未完成启动)。好吧,是的,不是。它可能尚未完成新过程的启动,但这不是行之有效的主要原因。
可以,因为node.js通过一个线程事件队列运行所有事件。因此,直到代码完成执行并将控制权返回给系统后,才能处理新产生的进程中的事件。只有这样,它才能处理事件队列中的下一个事件并触发要为其注册处理程序的事件之一。
或者,换句话说,来自另一个进程的事件都不是先发制人的。他们不会/不能中断您现有的Javascript代码。因此,由于您仍在运行Javascript代码,因此这些事件尚未运行。而是将它们放在事件队列中,直到您的Javascript代码完成,然后解释器才能从事件队列中获取下一个事件并运行与其关联的回调。同样,该回调将一直运行,直到返回到解释器,然后解释器才能获取下一个事件并运行其回调,依此类推...
这就是为什么node.js被称为事件驱动系统的原因。
这样,执行这种类型的结构就可以了:
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}`);
});
在完成代码并将控制权返回给系统之前,这些事件data
或close
事件均无法执行其回调。因此,像您一样设置这些事件处理程序是绝对安全的。即使新生成的进程正在运行并立即生成事件,这些事件也将一直位于事件队列中,直到您的Javascript完成其工作(包括设置事件处理程序)为止。
现在,如果您将事件处理程序的设置延迟到事件循环(如下面所示)的某个将来的滴答声(如下所示)setTimeout()
,那么您可能会错过一些事件:
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
setTimeout(() => {
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}`);
});
}, 10);
在这里,您并不是在事件循环的同一滴答中立即设置事件处理程序,而是经过短暂的延迟。因此,在安装事件处理程序之前,可以从事件循环中处理一些事件,并且您可能会错过其中一些事件。显然,您永远不会这样做(故意),但我只是想证明在事件循环的同一时间运行的代码没有问题,但是在事件循环的未来时间运行的代码可能有问题。缺少事件的问题。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句