节点和NPM运行脚本以及Ctrl-C两次触发SIGINT

时间:2019-02-16 10:31:57

标签: node.js npm npm-run

我在一个运行npm start的Nodejs应用程序中遇到了一个问题(只是node app.js)。

我的应用程序包含一个sigint处理程序,如下所示:

process.on('SIGINT', () => {
    db.disconnect().then({
        process.exit(0);
    }).catch(e => process.exit(1));
});

带有相应的日志。在对其他文件进行一些测试之后,我注意到在npm进程上进行Ctrl-C触发时,如果第一个退出退出的时间太长,则会两次触发SIGINT。 (尝试在示例应用程序上添加超时)。

现在,我添加了一个计数器来检查是否多次执行了呼叫,但是我不确定这是否是解决此问题的“方法”。我猜想npm进程上的SIGINT会在某个时间范围内退出,这就是为什么npm再过一次(总是只有两次)通过它的原因。

有人遇到这个问题并找到了可行的解决方案吗?

谢谢!

4 个答案:

答案 0 :(得分:3)

您可能想直接通过节点而不是通过npm start运行命令。 NPM可能会导致信号陷入更复杂的陷阱,请参见https://lisk.io/blog/tutorial/why-we-stopped-using-npm-start-child-processes

您的SIGINT处理程序可以多次调用,您应该编写代码来防止这种情况。

如果您正在运行父/子进程,请参见https://github.com/jtlapp/node-cleanup

按Ctrl-C组合键时,您会向SIGINT中的每个进程发送一个SIGINT信号。 当前的流程组。流程组是以下流程的集合: 所有这些都应该作为一个群体而结束而不是坚持下去 独立地。但是,某些程序(例如Emacs)会拦截并 重新调整SIGINT的用途,以便它不会结束该过程。在这种情况下 SIGINT不应结束该组的任何进程。

此外,在大多数时候不需要调用process.exit,请参见https://nodejs.org/api/process.html#process_process_exit_code

改为设置process.exitCode,删除信号处理程序,然后通过process.kill(process.pid, signal)重新引发信号

答案 1 :(得分:2)

我知道这篇文章已经存在了一段时间,但不幸的是我遇到了同样的问题。仅运行node <app.js>时,它仅触发一个信号。如果运行npm start,则该信号出现两次。 我建议检查服务器是否仍在侦听,而不是一个计数器,如果是,请继续执行逻辑和内部终止(例如db连接等):

process.on('SIGINT', function(){
 if (server.listening) {
    server.close(function (err) {
      if (err) {
        console.error(err);
        process.exit(1)
      }
      process.exit(0);
    })
 }
})

答案 2 :(得分:0)

假设以下代码位于名为 x.js 的文件中。
假设您在package.json文件中有一个名为 start 的npm脚本,该脚本执行 node x.js 命令。

process.on("SIGINT", (signal) => {
    console.log(signal + " signal received.");
    process.exitCode = 0;
});

setTimeout(function() {
    console.log("Hello");
}, 5000);

如果我们使用命令node x.js运行代码,则该代码将仅打印1行“收到的SIGINT信号”。但是,如果我们使用命令npm run start运行代码,则该代码将打印两行“收到的SIGINT信号”。除此之外,如果我们使用npm软件包nodemon来监视我们的代码,并且使用命令nodemon x.js运行该代码,该代码还将打印两行“收到的SIGINT信号”。 / p>

如果我们使用process.exit(0);而不是process.exitCode = 0;,我们会发现由于强制退出进程,上述所有命令仅会打印1行“收到SIGINT信号”。

如果我们不直接调用node命令并且在这种情况下使用诸如npmnodemon之类的层来触发node命令,则会导致这种解释该层两次将SIGINT信号传递到节点进程。

环境:
macOS Mojave v10.14.6
npm v6.10.0
节点v12.7.0
nodemon v1.18.11

答案 3 :(得分:0)

一个更简洁的解决方案是将断开连接的处理程序包装在一个 promise 中,并且由于 promise 内部实现,resolve 将仅被调用一次。

new Promise((resolve) => process.on('SIGINT', resolve))
   .then(() => db.disconnect())
   .then(() => process.exit(0))
   .catch(() => process.exit(1));

注意:我建议避免显式调用 process.exit() 并让节点自行退出。如果由于某些原因该进程没有退出,则意味着有什么东西阻止了它(例如:端口侦听、打开/挂起的 tcp 连接、未清除的 setIntervalsetTimeout 引用)