在快递中处理exception

我无法理解如何处理一些看起来像是expression一个非常基本的方面。 如果我有一些在asynchronouscallback中引发exception的代码,我不能捕捉到这个exception,因为try / catch块在callback运行时不再处于范围之内。 在这些情况下,浏览器将挂起,直到最终放弃说明服务器没有响应。 这是一个非常糟糕的用户体验。 我宁愿能够立即返回一个500错误的客户端。 默认的快速error handling程序显然不处理这种情况。 这是一些示例代码:

var express = require("express"); var app = express(); app.use(app.router); //express error handler (never called) app.use(function(err, req, res, next) { console.log(err); res.send(500); }); app.get("/test", function(req, res, next) { require("fs").readFile("/some/file", function(err, data) { ab(); //blow up }); }); app.listen(8888); 

在上面的代码中,ab()行会抛出“ReferenceError:a not defined”exception。 定义的error handling程序从不被调用。 请注意,在这种情况下,由fs.readFile()返回的err对象为null,因为文件已被正确读取。 错误是asynchronous处理程序中的代码。

我甚至读过关于使用node的uncaughtExpception的这篇文章 ,但文档说不使用那个方法。 即使我使用它,我又会如何将500响应发回给用户? 快递响应对象不再供我使用。

那么你如何处理这种情况呢?

好吧,我只是要发表一个完全不同的答案,因为我的第一个答案有一些有价值的信息,但事后看来是脱离主题。

简短的回答 :正确的事情是已经发生的事情:你的程序应该打印一个堆栈跟踪,并退出一个错误。

基本思路

所以我认为你需要考虑不同类别的错误。 我的第一个答案涉及数据相关的错误,一个精心编写的程序可以并且应该干净地处理。 你所描述的是一个CRASH。 如果您阅读了链接到的node.js文档,则是正确的。 在这一点上,程序可以做的唯一有用的事情是通过堆栈跟踪退出,并允许进程pipe理器重新启动并获得理解状态。 一旦你的程序崩溃了,它实质上是不可恢复的,因为错误的范围非常广泛,可能是exception进入堆栈顶部的根本原因。 在你的具体例子中,这个错误每次都会持续发生,直到源代码错误得到解决,应用程序被重新部署。 如果您担心未经testing的错误代码将进入您的应用程序,那么添加更多未经testing的error handling代码并不能真正解决正确的问题。

但是总之,不,没有办法获得引起这个exception的HTTP请求对象的引用,所以AFAIK不能改变浏览器中最终用户所感知的方式,除了在中间反向代理层在那里你可以configuration一个粗略的超时,并发送一个更友好的错误页面(这当然是没有用的任何请求是不是一个完整的HTML文档)。

节点中error handling的圣经

在我看来,Dave Pacheco 在Node.js中的error handling是这个主题的权威性工作。 它是全面的,广泛的,彻底的。 我build议定期阅读和重读。


要解决@ asparagino的评论,如果一个未处理的exception很容易重现或者发生频率很高,这不是一个边缘情况,这是一个错误。 正确的做法是改善你的代码,以防止在这种情况下产生未捕获的exception。 实际上,处理条件,从而将程序员错误转换为操作错误,并且您的程序可以继续而无需重新启动,并且没有未捕获的exception。

你应该通过app.use(error, req, res, next)使用express的error handling中间件。 Express维护一个单独的中间件堆栈,当正常的中间件堆栈抛出未捕获的exception时,它将使用它。 (注意,只是看看callback的参数(期望的参数的数量),将其分类为常规中间件或error handling中间件,这有点神奇,所以请记住,您必须声明上述参数以便明确理解这是一个error handling中间件)。

根据你的问题和意见,只要明白在node.js中exception并不是那么有用,因为每个asynchronous调用都会得到一个新的堆栈,这就是为什么callback在任何地方都被使用的原因,第一个参数是普遍的错误。 你在示例中的try/catch块只会捕获由findById直接抛出的exception(就像id是未定义的,因为它在你的代码片段中),但一旦实际调用数据库,就是这样,调用堆栈已经结束,直到一个完全不同的调用堆栈启动时,节点调用asynchronousIOcallback才能发生进一步的exception。

感谢您的答案,但这只适用于,如果我把try / catch在asynchronouscallback,并抓住下一步(exp)。 我想避免在每个asynchronouscallback中分开try / catch块。

不,这不是事实。 您不必手动调用next(exp) 。 Express会捕获错误并为您触发error handling中间件(无论如何,它都是开发人员友好的exception报告页面)。 即使在“正常”错误条件下,asynchronous库也不会抛出exception。 他们传递一个错误的callback,所以一般来说,你不必费心尝试/捕捉那么多的节点。 只是永远不要忽略传递给callback函数的错误参数,你没事。

在节点中看不到这个样板文件:

 someDb.query(someCriteria, function (error, result) { try { //some code to deal with result } catch (exception) { callback(exception); } }); 

你确实看到这个:

 someDb.query(someCriteria, function (error, result) { if (error) { callback(error); return; } //some code to deal with result }); 

节点处理IO的方式不同,这意味着调用堆栈的工作方式不同,这意味着exception工作方式不同,这意味着error handling工作方式不同 你可以编写一个稳定的节点/expression式应用程序来处理错误而不会崩溃,而不用写一个try / catch。 (expression有一个处理未被捕获的错误,一直到顶部的泡沫)。 这不是一个function限制,它只是asynchronousIO的一个后果,意味着你必须以不同的方式编写代码,并用callback而不是exception来处理错误。 把它看作是一种“限制”,而不是“这种方式”,就是给那些真正属于技术现实的东西赋予一个负面的含义。 在同步和asynchronous范例中都有干净而强大的exception处理模式。