更新: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) }- 对于基于回调的(即异步)代码,回调的第一个参数为
err
,如果发生错误err
则为错误,如果未发生错误err
则为null
。其他任何参数都遵循以下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 pre block d.run(function(){ // the asynchronous or synchronous pre that we want to catch thrown errors on var err = new Error('example') throw err })- 如果我们知道错误发生在哪里是同步代码,并且由于某种原因不能使用域(也许是旧版本的节点),则可以使用try catch语句:
// catch the uncaught errors in this synchronous pre block // try catch statements only work on synchronous pre try { // the synchronous pre 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与异步代码一起使用,则在运行Node 7.4或更高版本时,可以使用
async/await本机来编写异步函数。
要注意的另一件事
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)尝试捕获不需要的执行。在允许适当的线程而不是Javascript的异步事件机器风格的语言中,这不是问题。
- 最后,在未包裹域或try 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 pre that emits the otherwise uncaught error var err = new Error('example') throw err


