Node.js进程可以终止的原因有多种。其中一些是可以避免的,例如抛出错误时,而另一些则无法避免,例如内存不足。全局进程是一个EventEmitter实例,它在执行正常退出时发出退出事件。然后程序代码可以通过侦听此事件来执行最终的同步清理。下面是一些可以主动触发进程终止的方法:操作示例手动进程退出process.exit(1)UncaughtexceptionthrownewError()UnfulfilledpromisePromise.reject()IgnorederroreventEventEmitter#emit('error')Unhandledsignals$kill其中很多是意外触发的,比如未捕获的错误或未处理的承诺,但也有一些是为了直接终止进程而创建的。进程退出使用process.exit(code)来终止进程是最直接的方式。当您知道您的过程已达到生命终点时,这非常有用。代码值是可选的,默认值为0,最大值可以设置为255。0表示进程运行成功,而任何非零数字表示出现问题。这些值可以被许多不同的外部工具使用。例如,当运行测试套件时,非零值表示测试失败。直接调用process.exit()时,不会将隐式文本写入控制台。如果您编写的代码使用错误表示调用此方法,则您的代码应将错误输出给用户以帮助他们解决问题。例如,运行以下命令:$node-e"process.exit(42)"$echo$?在这种情况下,单行Node.js程序不会输出任何内容,尽管shell程序会打印退出状态。当这样的进程退出时,用户将无法理解发生了什么。所以参考下面的代码,当程序配置错误时会执行:进程.exit(1);}}在这种情况下,用户不清楚发生了什么。他们运行程序,将错误打印到控制台,然后他们能够纠正问题。process.exit()方法非常强大。尽管它在程序代码中有自己的用途,但它永远不应该真正引入可重用的库中。如果库中确实发生错误,则应该抛出错误,以便程序可以决定如何处理错误。exceprion、rejection和raisedError虽然process.exit()很有用,但对于运行时错误,您需要使用其他工具。例如,程序在处理一个HTTP请求时,一般错误不应该终止进程,而只是返回一个错误响应。有关错误发生位置的信息也很有用,这是应该抛出Error对象的位置。Error类的实例包含有关错误原因的有用元数据,例如堆栈跟踪和消息字符串。从Error扩展您自己的错误类是常见的做法。实例化Error本身没有太多副作用,如果发生错误则必须抛出。当使用throw关键字或发生某些逻辑错误时,将抛出错误。发生这种情况时,当前堆栈将“展开”,这意味着每个函数都会退出,直到调用函数将调用包装在try/catch语句中。遇到这条语句时,调用catch分支。如果错误未包含在try/catch中,则认为错误未被捕获。尽管您应该将throw关键字与Error一起使用,例如thrownewError('foo'),从技术上讲,你可以抛出任何东西。一旦有东西被抛出,它就被认为是异常。抛出Error实例很重要,因为捕获这些错误的代码很可能需要错误属性。Node.js内部库中常用的另一种模式是提供一个.code属性,它是一个字符串值,并且应该在各个版本之间保持一致。例如,错误的.code值是ERR_INVALID_URI,即使人类可读的.message属性可能会改变,但这个代码值不应该改变。遗憾的是,更常用的区分错误的模式是检查.message属性,该属性通常是动态的,因为可能需要修复拼写错误。这种方法有风险且容易出错。Node.js生态系统中没有完美的解决方案来区分所有库中的错误。当抛出未捕获的错误时,堆栈跟踪将打印到控制台,进程将以退出状态1终止。这是此类异常的示例:/tmp/foo.js:1thrownewTypeError('invalidfoo');^Error:invalidfooatObject.(/tmp/foo.js:2:11)...TRUNCATED...atinternal/main/run_main_module.js:17:47上面的堆栈跟踪片段显示错误发生在名为foo.js的文件的第2行第11列。全局进程是一个事件发射器,它通过监听uncaughtException事件来拦截未捕获的异常。下面是一个使用它的例子,在退出前拦截错误发送异步消息:constlogger=require('./lib/logger.js');process.on('uncaughtException',(error)=>{("发生了未捕获的异常",error,()=>{console.error(error);process.exit(1);});});Promise拒绝与抛出错误非常相似。如果调用了Promise的reject()方法,或者在异步函数中抛出错误,则Promise可以拒绝。在这方面,下面两个例子大致是等价的:Promise.reject(newError('ohno'));(async()=>{thrownewError('ohno');})();这是输出到控制台的消息:(node:52298)UnhandledPromiseRejectionWarning:E??rror:ohnoatObject.(/tmp/reject.js:1:16)...TRUNCATED...atinternal/main/run_main_module.js:17:47(node:52298)UnhandledPromiseRejectionWarning:未处理的承诺拒绝。这个错误要么是在没有catch块的情况下在异步函数内部抛出,要么是因为拒绝了一个没有用.catch()处理的承诺。与异常不同,从Node.jsv14开始,这些拒绝不会使进程崩溃。在未来的Node.js版本中,这将使当前进程崩溃。当这些未处理的拒绝发生时,您还可以拦截事件,监听流程对象上的另一个事件:process.on('unhandledRejection',(reason,promise)=>{});事件发射器是Node。在js中,许多对象实例都从这个基类扩展而来,并在库和程序中使用。他们非常受欢迎,值得与错误和拒绝一起讨论。当事件发射器发出没有侦听器的错误事件时,将抛出发出的参数。然后将抛出错误并退出进程:events.js:306throwerr;//未处理的“错误”事件^Error[ERR_UNHANDLED_ERROR]:未处理的错误。(undefined)在EventEmitter.emit(events.js:304:17)在Object.(/tmp/foo.js:1:40)...TRUNCATED...在internal/main/run_main_module.js:17:47{code:'ERR_UNHANDLED_ERROR',context:undefined}确保在您使用的事件发射器实例中侦听错误事件,以便您的程序可以正常处理事件而不会崩溃。信号信号是操作系统提供的一种机制,用于将数字消息从一个程序发送到另一个程序。这些数字通常由等效的常量字符串表示。例如,信号SIGKILL表示数字信号9。信号可以有不同的用途,但通常用于终止程序。不同的操作系统可以定义不同的信号,但是下面列表中的信号一般都是通用的:namenumber可以处理Node.js默认信号useSIGHUP1是终止父终端关闭SIGINT2是终止终端试图中断,按Ctrl+CSIGQUIT3YESTerminateTerminalistryingtoexit,pressCtrl+DSIGKILL9NoTerminate进程被强行终止SIGUSR110是启动调试器用户定义信号1SIGUSR212是终止用户定义信号2SIGTERM12是为了正常终止而终止SIGSTOP19如果程序没有终止进程被强制停止您可以选择实现信号处理程序,因此Handleable列为Yes。No的两个信号无法处理。Node.js默认值此列告诉您接收到信号时Node.js程序的默认操作是什么。最后的信号用法指定了信号的作用。在Node.js程序中处理这些信号可以通过监听进程对象上的更多事件来完成:#!/usr/bin/envnodeconsole.log(`ProcessID:${process.pid}`);process。on('SIGHUP',()=>console.log('Received:SIGHUP'));process.on('SIGINT',()=>console.log('Received:SIGINT'));setTimeout(()=>{},5*60*1000);//keepprocessalive在终端窗口中运行这个程序,然后按Ctrl+C,这个进程不会被终止。它将声明已收到SIGINT信号。切换到另一个终端窗口并根据输出的进程ID值执行以下命令:$kill-sSIGHUP这演示了一个程序如何向另一个程序发送信号,Node.js程序输出它接收到的SIGHUP信号。您可能已经猜到了,Node.js还可以向其他程序发送命令。以下命令可用于从临时Node.js进程向现有进程发送信号:$node-e"process.kill(,'SIGHUP')"这也将在您的第一个SIGHUP上执行消息显示在程序中。现在,如果您想终止第一个进程,请通过运行以下命令向它发送未处理的SIGKILL信号:$kill-9此时程序应该结束。这些信号通常在Node.js程序中用于处理正常关闭事件。例如,当KubernetesPod终止时,它会向程序发送SIGTERM信号,然后启动30秒计时器。然后程序可以在这30秒内优雅地自行关闭,关闭连接并保存数据。如果进程在此计时器后仍然存在,Kubernetes将向其发送SIGKILL。