几天前我刚开始试用node.js。我已经意识到,每当我的程序中出现未处理的异常时,节点就会终止。这与我所接触的正常服务器容器不同,在正常服务器容器中,当发生未处理的异常时,只有工作线程死亡,并且容器仍然能够接收请求。这引发了几个问题:

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

如果有任何指针/文章向我展示在node.js中处理未捕获异常的常见最佳实践,我将不胜感激


当前回答

  getCountryRegionData: (countryName, stateName) => {
    let countryData, stateData

    try {
      countryData = countries.find(
        country => country.countryName === countryName
      )
    } catch (error) {
      console.log(error.message)
      return error.message
    }

    try {
      stateData = countryData.regions.find(state => state.name === stateName)
    } catch (error) {
      console.log(error.message)
      return error.message
    }

    return {
      countryName: countryData.countryName,
      countryCode: countryData.countryShortCode,
      stateName: stateData.name,
      stateCode: stateData.shortCode,
    }
  },

其他回答

这里已经很好地讨论了捕获错误,但值得记住的是将错误记录在某个位置,以便您可以查看它们并修复它们。

​Bunyan是NodeJS的一个流行日志框架,它支持向一系列不同的输出位置进行输出,这使得它对本地调试非常有用,只要您避免console.log。​在域的错误处理程序中,可以将错误输出到日志文件中。

var log = bunyan.createLogger({
  name: 'myapp',
  streams: [
    {
      level: 'error',
      path: '/var/tmp/myapp-error.log'  // log ERROR to this file
    }
  ]
});

如果您有大量错误和/或服务器需要检查,这可能会很耗时,因此值得研究Raygun(免责声明,我在Raygun工作)这样的工具来将错误分组在一起,或者将两者同时使用。​如果您决定使用Raygun作为工具,那么设置也非常简单

var raygunClient = new raygun.Client().init({ apiKey: 'your API key' });
raygunClient.send(theError);

​在使用PM2或永久性工具的情况下,你的应用程序应该能够崩溃、注销发生的事情并重新启动,而不会出现任何重大问题。

前段时间读了这篇文章后,我想知道在api/函数级别上使用域进行异常处理是否安全。我想用它们来简化我编写的每个异步函数中的异常处理代码。我担心的是,为每个函数使用一个新域会引入大量开销。我的作业似乎表明开销最小,而且在某些情况下,域的性能实际上比try-catch要好。

http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/

nodejs域是处理nodejs中错误的最新方法。域可以捕获错误/其他事件以及传统抛出的对象。域还提供了处理回调的功能,其中错误通过intercept方法作为第一个参数传递。

与正常的try/catch风格的错误处理一样,通常最好在错误发生时抛出错误,并将您希望隔离错误的区域排除在外,以免影响代码的其余部分。“屏蔽”这些区域的方法是调用domain.run,将一个函数作为隔离代码块。

在同步代码中,以上内容就足够了——当发生错误时,要么让它被抛出,要么抓住它并在那里处理,还原需要还原的任何数据。

try {  
  //something
} catch(e) {
  // handle data reversion
  // probably log too
}

当错误发生在异步回调中时,您要么需要能够完全处理数据的回滚(共享状态、数据库等外部数据)。或者,您必须设置一些东西来指示发生了异常——如果您关心该标志,则必须等待回调完成。

var err = null;
var d = require('domain').create();
d.on('error', function(e) {
  err = e;
  // any additional error handling
}
d.run(function() { Fiber(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(err != null) {
    // handle data reversion
    // probably log too
  }

})});

上面的一些代码很难看,但您可以为自己创建模式以使其更漂亮,例如:

var specialDomain = specialDomain(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(specialDomain.error()) {
    // handle data reversion
    // probably log too
  } 
}, function() { // "catch"
  // any additional error handling
});

更新(2013-09):

上面,我使用了一个未来,它暗示了纤维语义,允许您在线等待未来。这实际上允许你在所有事情上使用传统的try-catch块,我认为这是最好的方法。然而,你不能总是这样做(即在浏览器中)。。。

还有一些未来不需要纤维语义(然后使用普通的浏览器JavaScript)。这些可以被称为期货、承诺或延期(我将从这里开始提及期货)。普通的旧JavaScript期货库允许在期货之间传播错误。只有其中的一些库允许正确处理任何抛出的未来,所以要小心。

例如:

returnsAFuture().then(function() {
  console.log('1')
  return doSomething() // also returns a future

}).then(function() {
  console.log('2')
  throw Error("oops an error was thrown")

}).then(function() {
  console.log('3')

}).catch(function(exception) {
  console.log('handler')
  // handle the exception
}).done()

这模拟了正常的try-catch,即使片段是异步的。它将打印:

1
2
handler

请注意,它不打印“3”,因为引发了中断该流的异常。

看看蓝鸟的承诺:

https://github.com/petkaantonov/bluebird

注意,除了这些库之外,我还没有找到其他许多库可以正确处理抛出的异常。jQuery的延迟,例如,不要-“fail”处理程序永远不会让“then”处理程序抛出异常,在我看来,这是一个交易破坏者。

我最近在http://snmaynard.com/2012/12/21/node-error-handling/.0.8版本中node的一个新功能是域,它允许您将所有形式的错误处理合并到一个更简单的管理表单中。您可以在我的文章中阅读它们。

您还可以使用Bugsnag之类的工具来跟踪未捕获的异常,并通过电子邮件、聊天室或创建未捕获异常的罚单(我是Bugsnag的联合创始人)获得通知。

  getCountryRegionData: (countryName, stateName) => {
    let countryData, stateData

    try {
      countryData = countries.find(
        country => country.countryName === countryName
      )
    } catch (error) {
      console.log(error.message)
      return error.message
    }

    try {
      stateData = countryData.regions.find(state => state.name === stateName)
    } catch (error) {
      console.log(error.message)
      return error.message
    }

    return {
      countryName: countryData.countryName,
      countryCode: countryData.countryShortCode,
      stateName: stateData.name,
      stateCode: stateData.shortCode,
    }
  },