如何在Node.js中等待子进程完成

58

我正在通过Node.js的子进程运行一个Python脚本,代码如下:

require('child_process').exec('python celulas.py', function (error, stdout, stderr) {
    child.stdout.pipe(process.stdout);
});

但是 Node 不会等待它完成。我该如何等待进程完成?

通过在主脚本调用的模块中运行子进程,是否有可能实现这一点?


可能是node.js同步执行系统命令的重复问题 - Jonathan Lonowski
3
大多数这些答案都不起作用,而且Nodejs文档很糟糕或根本不存在 :-/一个现代编程语言如何在如此基础的事情上遇到如此困难? - PJ Brunet
7个回答

110

使用exit事件来处理子进程。

var child = require('child_process').exec('python celulas.py')
child.stdout.pipe(process.stdout)
child.on('exit', function() {
  process.exit()
})

PS: 这并不是重复的问题,因为你只有在确实需要时才想要使用同步代码。


3
这是更好的答案。在 Node 中,尽可能避免阻塞(同步)代码 - 它会在等待子进程完成时停止整个程序。 - joews
还有一个 'end' 事件,可能为不同调用(exec()spawn()…)发出不同的事件。 - CodeManX
退出事件不能保证一定会被触发。例如,如果进程无法启动,则会调用错误事件。但是,如果进程已经启动并且出现了另一个错误,则两个处理程序都将被调用。 - Jeroen
这是正确的答案。当前的正确答案会阻止重要的子进程日志。 - Mohammad f
2
这是对另一个相反问题的答案。 - Andrew Koster
父进程不会在子进程完成之前退出吗? - ed22

37

NodeJS支持同步地执行此操作。
使用以下代码:

const execSync = require("child_process").execSync;
    
const result = execSync("python celulas.py");
    
// convert and show the output.
console.log(result.toString("utf8"));

记得将缓冲区转换为字符串。否则你将只得到十六进制代码。


20

在Node.js中等待进程结束的简单方法是:

const child = require('child_process').exec('python celulas.py')

await new Promise( (resolve) => {
    child.on('close', resolve)
})

7

您应该使用exec-sync

这可以让您的脚本等待执行完成

非常易于使用:

var execSync = require('exec-sync');

var user = execSync('python celulas.py');

看一下:https://www.npmjs.org/package/exec-sync

11
虽然这个答案是正确的,但我强烈建议不要在 node.js 中使用同步方法。这将完全阻塞应用程序直到命令完成。请参见 https://www.npmjs.org/package/execSync。它说不要在生产中使用这个。 - Bryan Johnson
2
我正在尝试弄清楚如何安装这个库,但是当我尝试在这个特定的库上运行npm install时,我得到了一长串错误。 - Akron
@BryanJohnson 或许提供一个使用建议会更有帮助。 - A. Wentzel
@A.Wentzel 我推荐使用 Alex 的回答中描述的非同步调用方法。execSync 可以用于简单的脚本,但是我似乎总是后悔使用同步方法。 - Bryan Johnson
2
exec-sync现在已经失效了(2019年)。只能使用“vanilla”方法。我在这个线程上发布了答案。 - Andrija Jostergård
它已经超过10年了,而且存储库不再维护 - 它今天将无法工作。相同的功能现在内置于node v0.12中:> https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options - MKLarsen

5

在我看来,处理这个问题的最佳方式是实现事件发射器。当第一个生成进程完成时,发出一个事件表示它已经完成。

const { spawn } = require('child_process');
const events = require('events');
const myEmitter = new events.EventEmitter();


firstSpawn = spawn('echo', ['hello']);
firstSpawn.on('exit', (exitCode) => {
    if (parseInt(exitCode) !== 0) {
        //Handle non-zero exit
    }
    myEmitter.emit('firstSpawn-finished');
}

myEmitter.on('firstSpawn-finished', () => {
    secondSpawn = spawn('echo', ['BYE!'])
})

3

即使您没有传递回调函数,您仍需要删除监听器以将安装添加到缓冲的标准输出和标准错误输出中,因为它仍会缓冲输出。在这种情况下,如果缓冲区已满,则Node仍将退出子进程。

var child = require('child_process').exec('python celulas.py');
child.stdout.removeAllListeners("data");
child.stderr.removeAllListeners("data");
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

3
你可以使用Util.promisify,例如:
const { exec } = require('child_process');
const Util = require('util');
const asyncExec = Util.promisify(exec);

asyncExec('python celulas.py')
.then((stdout, stderr) => {
    stdout.pipe(process.stdout);
    })
.catch(error => {
    console.log('error : ', error);
 });

孩子从哪里来? - Vignesh S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接