node.js - Node.js Best Practice Exception Handling

  显示原文与译文双语对照的内容

几天前我开始试用 node.js 。 我已经意识到当我在程序中遇到未处理的异常时,节点就会被终止。 这与普通的服务器容器不同,因为当未处理的异常发生并且容器仍然能够接收请求时,我已经暴露在工作线程上。 这引出了一些问题:

  • process.on('uncaughtException') 是防范它的唯一有效方法?
  • process.on('uncaughtException') 将在异步进程执行期间捕获未处理的异常?
  • 是否有一个模块已经构建了( 如发送电子邮件或者写入文件),我可以在未捕获的例外情况下利用它?

我希望任何指针/文章都能显示在 node.js 中处理未捕获异常的常见最佳实践

时间:

更新:Joyent现在有了自己的指南中提到这个答案。 以下信息更像是摘要:

安全"投掷"错误

理想情况下,我们希望尽可能避免出现未捕获的错误,这样,我们就可以使用以下方法之一安全地使用以下方法之一:

  • 对于同步代码,如果发生错误,返回错误:

    
    //Define divider as a syncrhonous function
    var divideSync = function(x,y) {
    //if error condition?
     if ( y === 0 ) {
    //"throw" the error safely by returning it
     return new Error("Can't divide by zero")
     }
     else {
    //no error occured, continue on
     return x/y
     }
    }
    
    //Divide 4/2
    var result = divideSync(4,2)
    //did an error occur?
    if ( result instanceof Error ) {
    //handle the error safely
     console.log('4/2=err', result)
    }
    else {
    //no error occured, continue on
     console.log('4/2='+result)
    }
    
    //Divide 4/0
    result = divideSync(4,0)
    //did an error occur?
    if ( result instanceof Error ) {
    //handle the error safely
     console.log('4/0=err', result)
    }
    else {
    //no error occured, continue on
     console.log('4/0='+result)
    }
    
    
  • 对于 callback-based ( IE 。异步) 代码,回调的第一个参数是 err,如果发生错误 err 是错误,如果错误,则 errnull 。 任何其他参数都遵循 err 参数:

    
    var divide = function(x,y,next) {
    //if error condition?
     if ( y === 0 ) {
    //"throw" the error safely by calling the completion callback
    //with the first argument being the error
     next(new Error("Can't divide by zero"))
     }
     else {
    //no error occured, continue on
     next(null, x/y)
     }
    }
    
    divide(4,2,function(err,result){
    //did an error occur?
     if ( err ) {
    //handle the error safely
     console.log('4/2=err', err)
     }
     else {
    //no error occured, continue on
     console.log('4/2='+result)
     }
    })
    
    divide(4,0,function(err,result){
    //did an error occur?
     if ( err ) {
    //handle the error safely
     console.log('4/0=err', err)
     }
     else {
    //no error occured, continue on
     console.log('4/0='+result)
     }
    })
    
    
  • 对于突发的代码,在任何地方发生错误,而不是抛出错误,激发 error 事件而不是:

    
    //Definite our Divider Event Emitter
    var events = require('events')
    var Divider = function(){
     events.EventEmitter.call(this)
    }
    require('util').inherits(Divider, events.EventEmitter)
    
    //Add the divide function
    Divider.prototype.divide = function(x,y){
    //if error condition?
     if ( y === 0 ) {
    //"throw" the error safely by emitting it
     var err = new Error("Can't divide by zero")
     this.emit('error', err)
     }
     else {
    //no error occured, continue on
     this.emit('divided', x, y, x/y)
     }
    
    //Chain
     return this;
    }
    
    //Create our divider and listen for errors
    var divider = new Divider()
    divider.on('error', function(err){
    //handle the error safely
     console.log(err)
    })
    divider.on('divided', function(x,y,result){
     console.log(x+'/'+y+'='+result)
    })
    
    //Divide
    divider.divide(4,2).divide(4,0)
    
    

安全"正在捕获"错误

虽然有时也会导致让某个地方,那里仍然有代码,它抛出一个错误捕获的异常和一个潜在的崩溃对我们的应用程序安全地如果我们不抓住它。 根据我们的代码架构,可以使用以下方法之一捕捉它:

  • 当我们知道发生错误的地方时,我们可以将该部分包装在 node.js 域中

    
    var d = require('domain').create()
    d.on('error', function(err){
    //handle the error safely
     console.log(err)
    })
    
    //catch the uncaught errors in this asynchronous or synchronous code block
    d.run(function(){
    //the asynchronous or synchronous code that we want to catch thrown errors on
     var err = new Error('example')
     throw err
    })
    
    
  • 如果我们知道该错误是在哪里发生( 可能是旧版本的节点) 是同步的代码,但由于各种原因而不能使用域,我们可以使用trycatch语句:

    
    //catch the uncaught errors in this synchronous code block
    //try catch statements only work on synchronous code
    try {
    //the synchronous code that we want to catch thrown errors on
     var err = new Error('example')
     throw err
    } catch (err) {
    //handle the error safely
     console.log(err)
    }
    
    

    但是,注意不要在异步代码中使用 try...catch,因为异步抛出的错误不会被捕获:

    
    try {
     setTimeout(function(){
     var err = new Error('example')
     throw err
     }, 1000)
    }
    catch (err) {
    //Example error won't be caught here... crashing our app
    //hence the need for domains
    }
    
    

    使用 try...catch 时要小心的另一件事是,在 try 语句中封装完成回调的风险如下:

    
    var divide = function(x,y,next) {
    //if error condition?
     if ( y === 0 ) {
    //"throw" the error safely by calling the completion callback
    //with the first argument being the error
     next(new Error("Can't divide by zero"))
     }
     else {
    //no error occured, continue on
     next(null, x/y)
     }
    }
    
    var continueElsewhere = function(err, result){
     throw new Error('elsewhere has failed')
    }
    
    try {
     divide(4, 2, continueElsewhere)
    //^ the execution of divide, and the execution of 
    //continueElsewhere will be inside the try statement
    }
    catch (err) {
     console.log(err.stack)
    //^ will output the"unexpected" result of: elsewhere has failed
    }
    
    

    当你的代码变得更加复杂时,这个问题很容易解决。 同样地,以避免( 1 ) 既,最好使用域或者返回错误未捕获的异常在异步代码( 2 ) trycatch捕捉执行,你不希望它。 在允许适当的线程而不是异步的javascript event-machine风格的语言中,这是一个问题。

  • 最后,在未打包在域或者尝试catch语句的地方发生未捕获的错误时,我们可以使用 uncaughtException 侦听器( 但是这样做可以使应用程序处于未知状态 ) 使应用程序不会崩溃:

    
    //catch the uncaught errors that weren't wrapped in a domain or try catch statement
    //do not use this in modules, but only in applications, as otherwise we could have multiple of these bound
    process.on('uncaughtException', function(err) {
    //handle the error safely
     console.log(err)
    })
    
    //the asynchronous or synchronous code that emits the otherwise uncaught error
    var err = new Error('example')
    throw err
    
    

你可以捕获未捕获的异常,但它的用途有限。 参见 http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb web

monitforever 或者 upstart 在崩溃时可以用来重启节点进程。 优雅的关闭是最好的选择( 例如。 将所有in-memory数据保存到未捕获的异常处理程序中。

使用try-catch的一个实例可能是使用forEach循环。 它是同步的,但同时你不能在内部作用域中使用return语句。 相反,可以使用尝试和捕捉方法在适当范围内返回一个错误对象。 请考虑:


function processArray() {
 try { 
 [1, 2, 3].forEach(function() { throw new Error('exception'); }); 
 } catch (e) { 
 return e; 
 }
}

这是 @balupton 上面描述的方法的组合。

...