Node.js最佳实践异常处理

几天前我刚开始试用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=新错误(“示例”)
    失误
    

发表评论