404

我想告诉 Node.js 总是在它退出之前做一些事情,无论出于何种原因—— Ctrl+ C、异常或任何其他原因。

我试过这个:

process.on('exit', function (){
    console.log('Goodbye!');
});

我开始了这个过程,杀死了它,什么也没发生。我再次启动它,按Ctrl+ C,仍然没有任何反应......

4

12 回答 12

626

更新:

process.on('exit')您可以为任何其他情况(SIGINT或未处理的异常)注册一个处理程序以调用process.exit()

process.stdin.resume();//so the program will not close instantly

function exitHandler(options, exitCode) {
    if (options.cleanup) console.log('clean');
    if (exitCode || exitCode === 0) console.log(exitCode);
    if (options.exit) process.exit();
}

//do something when app is closing
process.on('exit', exitHandler.bind(null,{cleanup:true}));

//catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, {exit:true}));

// catches "kill pid" (for example: nodemon restart)
process.on('SIGUSR1', exitHandler.bind(null, {exit:true}));
process.on('SIGUSR2', exitHandler.bind(null, {exit:true}));

//catches uncaught exceptions
process.on('uncaughtException', exitHandler.bind(null, {exit:true}));
于 2012-12-25T18:17:56.537 回答
190

下面的脚本允许对所有退出条件使用一个处理程序。它使用特定于应用程序的回调函数来执行自定义清理代码。

清理.js

// Object to capture process exits and call app specific cleanup function

function noOp() {};

exports.Cleanup = function Cleanup(callback) {

  // attach user callback to the process event emitter
  // if no callback, it will still exit gracefully on Ctrl-C
  callback = callback || noOp;
  process.on('cleanup',callback);

  // do app specific cleaning before exiting
  process.on('exit', function () {
    process.emit('cleanup');
  });

  // catch ctrl+c event and exit normally
  process.on('SIGINT', function () {
    console.log('Ctrl-C...');
    process.exit(2);
  });

  //catch uncaught exceptions, trace, then exit normally
  process.on('uncaughtException', function(e) {
    console.log('Uncaught Exception...');
    console.log(e.stack);
    process.exit(99);
  });
};

此代码拦截未捕获的异常、Ctrl+C和正常退出事件。然后它在退出之前调用一个可选的用户清理回调函数,用一个对象处理所有退出条件。

该模块只是扩展了流程对象,而不是定义另一个事件发射器。如果没有特定于应用程序的回调,则清理默认为无操作函数。这足以让我在通过Ctrl+退出时让子进程继续运行的情况下使用C

您可以根据需要轻松添加其他退出事件,例如 SIGHUP。注意:根据 NodeJS 手册,SIGKILL 不能有监听器。下面的测试代码演示了使用 cleanup.js 的各种方式

// test cleanup.js on version 0.10.21

// loads module and registers app specific cleanup callback...
var cleanup = require('./cleanup').Cleanup(myCleanup);
//var cleanup = require('./cleanup').Cleanup(); // will call noOp

// defines app specific callback...
function myCleanup() {
  console.log('App specific cleanup code...');
};

// All of the following code is only needed for test demo

// Prevents the program from closing instantly
process.stdin.resume();

// Emits an uncaught exception when called because module does not exist
function error() {
  console.log('error');
  var x = require('');
};

// Try each of the following one at a time:

// Uncomment the next line to test exiting on an uncaught exception
//setTimeout(error,2000);

// Uncomment the next line to test exiting normally
//setTimeout(function(){process.exit(3)}, 2000);

// Type Ctrl-C to test forced exit 
于 2014-02-22T00:25:11.503 回答
52

这会捕获我能找到的每个可以处理的退出事件。到目前为止看起来相当可靠和干净。

[`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach((eventType) => {
  process.on(eventType, cleanUpServer.bind(null, eventType));
})
于 2018-03-20T19:29:27.540 回答
22

“exit”是一个事件,当节点在内部完成它的事件循环时触发,当你在外部终止进程时它不会被触发。

您正在寻找的是在 SIGINT 上执行某些操作。

http://nodejs.org/api/process.html#process_signal_events上的文档给出了一个例子:

监听 SIGINT 的示例:

// Start reading from stdin so we don't exit.
process.stdin.resume();

process.on('SIGINT', function () {
  console.log('Got SIGINT.  Press Control-D to exit.');
});

注意:这似乎会中断 sigint,您需要在完成代码时调用 process.exit()。

于 2012-12-25T20:11:02.790 回答
11
function fnAsyncTest(callback) {
    require('fs').writeFile('async.txt', 'bye!', callback);
}

function fnSyncTest() {
    for (var i = 0; i < 10; i++) {}
}

function killProcess() {

    if (process.exitTimeoutId) {
        return;
    }

    process.exitTimeoutId = setTimeout(() => process.exit, 5000);
    console.log('process will exit in 5 seconds');

    fnAsyncTest(function() {
        console.log('async op. done', arguments);
    });

    if (!fnSyncTest()) {
        console.log('sync op. done');
    }
}

// https://nodejs.org/api/process.html#process_signal_events
process.on('SIGTERM', killProcess);
process.on('SIGINT', killProcess);

process.on('uncaughtException', function(e) {

    console.log('[uncaughtException] app will be terminated: ', e.stack);

    killProcess();
    /**
     * @https://nodejs.org/api/process.html#process_event_uncaughtexception
     *  
     * 'uncaughtException' should be used to perform synchronous cleanup before shutting down the process. 
     * It is not safe to resume normal operation after 'uncaughtException'. 
     * If you do use it, restart your application after every unhandled exception!
     * 
     * You have been warned.
     */
});

console.log('App is running...');
console.log('Try to press CTRL+C or SIGNAL the process with PID: ', process.pid);

process.stdin.resume();
// just for testing
于 2016-02-02T18:26:19.403 回答
7

只想death在这里提一下包:https ://github.com/jprichardson/node-death

例子:

var ON_DEATH = require('death')({uncaughtException: true}); //this is intentionally ugly

ON_DEATH(function(signal, err) {
  //clean up code here
})
于 2016-08-12T19:38:25.970 回答
6

async-exit-hook似乎是处理这个问题的最新解决方案。它是exit-hook的分叉/重写版本,在退出之前支持异步代码。

于 2020-09-10T19:16:17.463 回答
3

我需要在退出时执行异步清理操作,这个问题中的任何答案都不适合我。

于是我自己试了一下,终于找到了这个:

process.once('uncaughtException', async () => {
  await cleanup()

  process.exit(0)
})

process.once('SIGINT', () => { throw new Error() })
于 2021-03-28T12:56:06.173 回答
1

在玩弄了其他答案之后,这是我完成此任务的解决方案。实施这种方式有助于我将清理集中在一个地方,防止重复处理清理。

  1. 我想将所有其他退出代码路由到“退出”代码。
const others = [`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`]
others.forEach((eventType) => {
    process.on(eventType, exitRouter.bind(null, { exit: true }));
})
  1. exitRouter 所做的是调用 process.exit()
function exitRouter(options, exitCode) {
   if (exitCode || exitCode === 0) console.log(`ExitCode ${exitCode}`);
   if (options.exit) process.exit();
}
  1. 在“退出”时,使用新功能处理清理
function exitHandler(exitCode) {
  console.log(`ExitCode ${exitCode}`);
  console.log('Exiting finally...')
}

process.on('exit', exitHandler)

出于演示目的,这是指向我的要点的链接。在文件中,我添加了一个 setTimeout 来伪造正在运行的进程。

如果您运行node node-exit-demo.js并且什么也不做,那么 2 秒后,您会看到日志:

The service is finish after a while.
ExitCode 0
Exiting finally...

否则,如果在服务完成之前,您通过 终止ctrl+C,您将看到:

^CExitCode SIGINT
ExitCode 0
Exiting finally...

发生的事情是 Node 进程最初以代码 SIGINT 退出,然后它路由到 process.exit(),最后以退出代码 0 退出。

于 2020-06-08T16:41:36.047 回答
0

io.js有一个exit和一个beforeExit事件,可以做你想做的事。

于 2015-01-31T15:53:28.053 回答
-1

如果该进程是由另一个节点进程生成的,例如:

var child = spawn('gulp', ['watch'], {
    stdio: 'inherit',
});

稍后您尝试通过以下方式杀死它:

child.kill();

这就是您处理事件的方式 [on the child]:

process.on('SIGTERM', function() {
    console.log('Goodbye!');
});
于 2016-04-20T23:12:57.073 回答
-2

这是 Windows 的一个不错的 hack

process.on('exit', async () => {
    require('fs').writeFileSync('./tmp.js', 'crash', 'utf-8')
});
于 2020-04-19T05:44:18.843 回答