几天前我刚开始试用node.js。我意识到,每当我的程序中出现未处理的异常时,节点就会终止。这与我接触过的普通服务器容器不同,在这里,当发生未处理的异常时,只有工作线程死亡,并且容器仍然能够接收请求。这引发了几个问题:
process.on('uncaughtException')是防止它的唯一有效方法吗process.on('uncaughtException')是否也会在异步进程执行期间捕获未处理的异常- 是否有一个已经构建的模块(如发送电子邮件或写入文件),我可以在未捕获的异常情况下利用它
如果有任何文章能向我展示node.js中处理未捕获异常的常见最佳实践,我将不胜感激
更新:Joyent现在有了自己的指南。以下信息更像是一个总结:
安全地“抛出”错误
理想情况下,我们希望尽可能避免未捕获的错误,因此,我们可以根据我们的代码体系结构,使用以下方法之一安全地“抛出”错误,而不是直接抛出错误:
-
对于同步代码,如果发生错误,则返回错误:
//将除法器定义为同步函数 var divideSync=函数(x,y){ //如果是错误条件? 如果(y==0){ //通过返回错误来安全地“抛出”错误 返回新错误(“不能被零除”) } 否则{ //没有发生错误,请继续 返回x/y } } //除以4/2 var结果=除法同步(4,2) //发生错误了吗? 如果(错误的结果实例){ //安全地处理错误 console.log('4/2=err',结果) } 否则{ //没有发生错误,请继续 console.log('4/2='+结果) } //除以4/0 结果=除法同步(4,0) //发生错误了吗? 如果(错误的结果实例){ //安全地处理错误 console.log('4/0=err',结果) } 否则{ //没有发生错误,请继续 console.log('4/0='+结果) } -
对于基于回调的(即异步)代码,回调的第一个参数是
err,如果发生错误err就是错误,如果没有发生错误err就是null。任何其他参数都跟在err参数后面:var divide=函数(x,y,next){ //如果是错误条件? 如果(y==0){ //通过调用完成回调来安全地“抛出”错误 //第一个参数是错误 下一步(新错误(“不能被零除”)) } 否则{ //没有发生错误,请继续 下一个(空,x/y) } } 除法(4,2,函数(错误,结果){ //发生错误了吗? 如果(错误){ //安全地处理错误 console.log('4/2=err',err) } 否则{ //没有发生错误,请继续 console.log('4/2='+结果) } }) 除法(4,0,函数(错误,结果){ //发生错误了吗? 如果(错误){ //安全地处理错误 console.log('4/0=err',err) } 否则{ //没有发生错误,请继续 console.log('4/0='+结果) } }) -
对于可能在任何地方发生错误的事件代码,不要抛出错误,而是触发
error事件://确定除法器事件发射器 var events=require('事件') 变量除法器=函数(){ events.EventEmitter.call(此) } require('util')。继承(分隔符、events.EventEmitter) //添加divide函数 Divider.prototype.divide=函数(x,y){ //如果是错误条件? 如果(y==0){ //通过发出错误来安全地“抛出”错误 var err=新错误(“不能被零除”) this.emit('error',err) } 否则{ //没有发生错误,请继续 此.emit('divided',x,y,x/y) } //链子 归还这个; } //创建分隔器并侦听错误 var除法器=新除法器() 除法器.on('error',函数(err){ //安全地处理错误 console.log(错误) }) 除法器开('divided',函数(x,y,result){ console.log(x+'/'+y+'='+结果) }) //分开 除法器。除法器(4,2)。除法器(4,0)
安全地“捕获”错误
但有时,仍然可能有代码在某个地方抛出错误,如果我们不能安全地捕获它,可能会导致未捕获的异常和应用程序的潜在崩溃。根据我们的代码体系结构,我们可以使用以下方法之一捕获它:
-
当我们知道错误发生在哪里时,我们可以将该部分包装到node.js域中
var d=require(’domain’)。create()
d、 on(’error’,函数(err){
//安全地处理错误
console.log(错误)
})
//捕获此异步或同步代码块中未捕获的错误
d、 运行(函数(){
//我们希望捕获抛出错误的异步或同步代码
var err=新错误(“示例”)
失误
})
-
如果我们知道发生错误的地方是同步代码,并且出于任何原因不能使用域(可能是旧版本的节点),我们可以使用try-catch语句:
//捕获此同步代码块中未捕获的错误 //try-catch语句仅对同步代码有效 试一试{ //我们希望捕获抛出错误的同步代码 var err=新错误(“示例”) 失误 }捕捉(错误){ //安全地处理错误 console.log(错误) }但是,注意不要在异步代码中使用
try…catch,因为异步抛出的错误不会被捕获:试试看{ setTimeout(函数(){ var err=新错误(“示例”) 失误 }, 1000) } 捕捉(错误){ //此处不会捕获示例错误…正在使我们的应用程序崩溃 //因此需要域 }如果您确实希望使用
try..catch并使用异步代码,则在运行Node 7.4或更高版本时,您可以使用async/wait本机编写异步函数使用
try…catch时要注意的另一件事是在try语句中包装完成回调的风险,如下所示:var divide=函数(x,y,next){ //如果是错误条件? 如果(y==0){ //通过调用完成回调来安全地“抛出”错误 //第一个参数是错误 下一步(新错误(“不能被零除”)) } 否则{ //没有发生错误,请继续 下一个(空,x/y) } } var continueElsewhere=函数(错误,结果){ 抛出新错误('别处已失败') } 试一试{ 除法(4,2,continueElsewhere) //^divide的执行,以及 //continueElsewhere将位于try语句中 } 捕捉(错误){ console.log(err.stack) //^将输出“意外”结果:别处已失败 }当你的代码变得更复杂时,这很容易做到。因此,最好使用域或返回错误以避免(1)异步代码中未捕获的异常(2)您不希望执行的try-catch捕获执行。在允许正确线程而不是JavaScript异步事件机风格的语言中,这不是一个问题
-
最后,如果未捕获错误发生在未包装在域或try-catch语句中的位置,我们可以使用
uncaughtException侦听器使应用程序不会崩溃(但是这样做会使应用程序处于未知状态)://捕获未包装在域中的未捕获错误,或重试catch语句 //不要在模块中使用它,而只能在应用程序中使用,否则我们可能会有多个这样的绑定 process.on('uncaughtException',函数(err){ //安全地处理错误 console.log(错误) }) //发出其他未捕获错误的异步或同步代码 var err=新错误(“示例”) 失误