Javascript 异步 node.js 调用中的错误处理
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5816436/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
error handling in asynchronous node.js calls
提问by Udo G
I'm new to node.js although I'm pretty familiar with JavaScript in general. My question is regarding "best practices" on how to handle errors in node.js.
我是 node.js 的新手,虽然我对 JavaScript 非常熟悉。我的问题是关于如何处理 node.js 中的错误的“最佳实践”。
Normally when programming web servers, FastCGI servers or web pages in various languages I'm using Exceptions with blocking handlers in a multi-threading environment. When a request comes in I usually do something like this:
通常,在以各种语言编写 Web 服务器、FastCGI 服务器或网页时,我在多线程环境中使用带有阻塞处理程序的异常。当一个请求进来时,我通常会做这样的事情:
function handleRequest(request, response) {
try {
if (request.url=="whatever")
handleWhateverRequest(request, response);
else
throw new Error("404 not found");
} catch (e) {
response.writeHead(500, {'Content-Type': 'text/plain'});
response.end("Server error: "+e.message);
}
}
function handleWhateverRequest(request, response) {
if (something)
throw new Error("something bad happened");
Response.end("OK");
}
This way I can always handle internal errors and send a valid response to the user.
通过这种方式,我可以始终处理内部错误并向用户发送有效响应。
I understand that with node.js one is supposed to do non-blocking calls which obviously leads to various number of callbacks, like in this example:
我知道使用 node.js 应该进行非阻塞调用,这显然会导致不同数量的回调,就像在这个例子中一样:
var sys = require('sys'),
fs = require('fs');
require("http").createServer(handleRequest).listen(8124);
function handleRequest(request, response) {
fs.open("/proc/cpuinfo", "r",
function(error, fd) {
if (error)
throw new Error("fs.open error: "+error.message);
console.log("File open.");
var buffer = new require('buffer').Buffer(10);
fs.read(fd, buffer, 0, 10, null,
function(error, bytesRead, buffer) {
buffer.dontTryThisAtHome(); // causes exception
response.end(buffer);
}); //fs.read
}); //fs.open
}
This example will kill the server completely because exceptions aren't being catched. My problem is here that I can't use a single try/catch anymore and thus can't generally catch any error that may be raised during the handling of the request.
此示例将完全终止服务器,因为未捕获异常。我的问题是我不能再使用单个 try/catch,因此通常无法捕获在处理请求期间可能出现的任何错误。
Of course I could add a try/catch in each callback but I don't like that approach because then it's up to the programmer that he doesn't forgeta try/catch. For a complex server with lots of different and complex handlers this isn't acceptable.
当然,我可以在每个回调中添加一个 try/catch,但我不喜欢这种方法,因为这取决于程序员他不会忘记一个 try/catch。对于具有许多不同且复杂的处理程序的复杂服务器,这是不可接受的。
I could use a global exception handler (preventing the complete server crash) but then I can't send a response to the user since I don't know which request lead to the exception. This also means that the request remains unhandled/open and the browser is waiting forever for a response.
我可以使用全局异常处理程序(防止整个服务器崩溃),但是我无法向用户发送响应,因为我不知道哪个请求导致异常。这也意味着请求保持未处理/打开状态,浏览器一直在等待响应。
Does someone have a good, rock solid solution?
有人有一个好的,坚如磐石的解决方案吗?
采纳答案by Sam Shiles
Node 0.8 introduces a new concept called "Domains". They are very roughly analogousness to AppDomains in .net and provide a way of encapsulating a group of IO operations. They basically allow you to wrap your request processing calls in a context specific group. If this group throws any uncaught exceptions then they can be handled and dealt with in a manner which gives you access to all the scope and context specific information you require in order to successfully recover from the error (if possible).
Node 0.8 引入了一个称为“域”的新概念。它们与 .net 中的 AppDomains 非常相似,并提供了一种封装一组 IO 操作的方法。它们基本上允许您将请求处理调用包装在特定于上下文的组中。如果该组抛出任何未捕获的异常,则可以通过某种方式处理和处理它们,该方式使您可以访问所需的所有范围和上下文特定信息,以便成功从错误中恢复(如果可能)。
This feature is new and has only just been introduced, so use with caution, but from what I can tell it has been specifically introduced to deal with the problem which the OP is trying to tackle.
此功能是新功能,刚刚引入,因此请谨慎使用,但据我所知,它是专门用于处理 OP 试图解决的问题的。
Documentation can be found at: http://nodejs.org/api/domain.html
可以在以下位置找到文档:http: //nodejs.org/api/domain.html
回答by 3rdEden
Checkout the uncaughtException
handler in node.js. It captures the thrown errors that bubble up to the event loop.
签出uncaughtException
node.js 中的处理程序。它捕获冒泡到事件循环的抛出错误。
http://nodejs.org/docs/v0.4.7/api/process.html#event_uncaughtException_
http://nodejs.org/docs/v0.4.7/api/process.html#event_uncaughtException_
But not throwing errors is always a better solution. You could just do a return res.end('Unabled to load file xxx');
但不抛出错误总是更好的解决方案。你可以做一个return res.end('Unabled to load file xxx');
回答by chjj
This is one of the problems with Node right now. It's practically impossible to track down which request caused an error to be thrown inside a callback.
这是 Node 目前存在的问题之一。实际上不可能追踪哪个请求导致在回调中抛出错误。
You're going to have to handle your errors within the callbacks themselves (where you still have a reference to the request and response objects), if possible. The uncaughtException handler will stop the node process from exiting, but the request that caused the exception in the first place will just hang there from the user point of view.
如果可能的话,您将不得不在回调本身中处理您的错误(您仍然可以引用请求和响应对象)。uncaughtException 处理程序将阻止节点进程退出,但从用户的角度来看,首先导致异常的请求将挂在那里。
回答by Udo G
I give an answer to my own question... :)
我回答了我自己的问题... :)
As it seems there is no way around to manually catch errors. I now use a helper function that itself returns a functioncontaining a try/catch block. Additionally, my own web server class checks if either the request handling function calls response.end()
orthe try/catch helper function waitfor()
(raising an exception otherwise). This avoids to a great extent that request are mistakenly left unprotected by the developer. It isn't a 100% error-prone solution but works well enough for me.
似乎没有办法手动捕获错误。我现在使用一个辅助函数,它本身返回一个包含 try/catch 块的函数。此外,我自己的 Web 服务器类检查请求处理函数调用response.end()
或try/catch 帮助函数waitfor()
(否则引发异常)。这在很大程度上避免了请求错误地不受开发人员保护。这不是一个 100% 容易出错的解决方案,但对我来说效果很好。
handler.waitfor = function(callback) {
var me=this;
// avoid exception because response.end() won't be called immediately:
this.waiting=true;
return function() {
me.waiting=false;
try {
callback.apply(this, arguments);
if (!me.waiting && !me.finished)
throw new Error("Response handler returned and did neither send a "+
"response nor did it call waitfor()");
} catch (e) {
me.handleException(e);
}
}
}
This way I just have to add a inline waitfor()
call to be on the safe side.
这样我只需要添加一个内联waitfor()
调用就安全了。
function handleRequest(request, response, handler) {
fs.read(fd, buffer, 0, 10, null, handler.waitfor(
function(error, bytesRead, buffer) {
buffer.unknownFunction(); // causes exception
response.end(buffer);
}
)); //fs.read
}
The actual checking mechanism is a little more complex, but it should be clear how it works. If someone is interested I can post the full code here.
实际的检查机制稍微复杂一些,但它是如何工作的应该很清楚。如果有人感兴趣,我可以在这里发布完整的代码。
回答by Marian Galik
Very good question. I'm dealing with the same problem now. Probably the best way, would be to use uncaughtException
. The reference to respone and request objects is not the problem, because you can wrap them into your exception object, that is passed to uncaughtException
event. Something like this:
很好的问题。我现在正在处理同样的问题。可能最好的方法是使用uncaughtException
. 对响应和请求对象的引用不是问题,因为您可以将它们包装到您的异常对象中,即传递给uncaughtException
事件。像这样的东西:
var HttpException = function (request, response, message, code) {
this.request = request;
this.response = response;
this.message = message;
this.code = code || 500;
}
Throw it:
丢它:
throw new HttpException(request, response, 'File not found', 404);
And handle the response:
并处理响应:
process.on('uncaughtException', function (exception) {
exception.response.writeHead(exception.code, {'Content-Type': 'text/html'});
exception.response.end('Error ' + exception.code + ' - ' + exception.message);
});
I haven't test this solution yet, but I don't see the reason why this couldn't work.
我还没有测试过这个解决方案,但我看不出这不起作用的原因。
回答by jslatts
One idea: You could just use a helper method to create your call backs and make it your standard practice to use it. This does put the burden on the developer still, but at least you can have a "standard" way of handling your callbacks such that the chance of forgetting one is low:
一个想法:您可以使用辅助方法来创建您的回调,并使其成为使用它的标准做法。这确实给开发人员带来了负担,但至少你可以有一种“标准”的方式来处理你的回调,这样忘记一个的可能性很低:
var callWithHttpCatch = function(response, fn) {
try {
fn && fn();
}
catch {
response.writeHead(500, {'Content-Type': 'text/plain'}); //No
}
}
<snipped>
var buffer = new require('buffer').Buffer(10);
fs.read(fd, buffer, 0, 10, null,
function(error, bytesRead, buffer) {
callWithHttpCatch(response, buffer.dontTryThisAtHome()); // causes exception
response.end(buffer);
}); //fs.read
}); //fs.open
I know that probably isn't the answer you were looking for, but one of the nice things about ECMAScript (or functional programming in general) is how easily you can roll your own tooling for things like this.
我知道这可能不是您正在寻找的答案,但是 ECMAScript(或一般的函数式编程)的优点之一是您可以轻松地为此类事情推出自己的工具。
回答by SilentSteel
At the time of this writing, the approach I am seeing is to use "Promises".
在撰写本文时,我看到的方法是使用“Promises”。
http://howtonode.org/promises
https://www.promisejs.org/
http://howtonode.org/promises
https://www.promisejs.org/
These allow code and callbacks to be structured well for error management and also makes it more readable. It primarily uses the .then()function.
这些允许代码和回调结构良好以进行错误管理,并使其更具可读性。它主要使用.then()函数。
someFunction().then(success_callback_func, failed_callback_func);
Here's a basic example:
这是一个基本示例:
var SomeModule = require('someModule');
var success = function (ret) {
console.log('>>>>>>>> Success!');
}
var failed = function (err) {
if (err instanceof SomeModule.errorName) {
// Note: I've often seen the error definitions in SomeModule.errors.ErrorName
console.log("FOUND SPECIFIC ERROR");
}
console.log('>>>>>>>> FAILED!');
}
someFunction().then(success, failed);
console.log("This line with appear instantly, since the last function was asynchronous.");
回答by Yonatan
Also in synchronous multi-threaded programming (e.g. .NET, Java, PHP) you can't return any meaningful information to the client when a custom unkown Exception is caught. You may just return HTTP 500 when you have no info regarding the Exception.
同样在同步多线程编程(例如.NET、Java、PHP)中,当捕获到自定义的未知异常时,您无法向客户端返回任何有意义的信息。当您没有关于异常的信息时,您可能只返回 HTTP 500。
Thus, the 'secret' lies in filling a descriptive Error object, this way your error handler can map from the meaningful error to the right HTTP status + optionally a descriptive result. However you must also catch the exception before it arrives to process.on('uncaughtException'):
因此,“秘密”在于填充一个描述性的 Error 对象,这样你的错误处理程序可以从有意义的错误映射到正确的 HTTP 状态 + 可选的描述性结果。但是,您还必须在异常到达 process.on('uncaughtException') 之前捕获异常:
Step1: Define a meaningful error object
Step1:定义一个有意义的错误对象
function appError(errorCode, description, isOperational) {
Error.call(this);
Error.captureStackTrace(this);
this.errorCode = errorCode;
//...other properties assigned here
};
appError.prototype.__proto__ = Error.prototype;
module.exports.appError = appError;
Step2: When throwing an Exception, fill it with properties (see step 1) that allows the handler to convert it to meannigul HTTP result:
步骤 2:当抛出异常时,用允许处理程序将其转换为平均 HTTP 结果的属性(参见步骤 1)填充它:
throw new appError(errorManagement.commonErrors.resourceNotFound, "further explanation", true)
Step3: When invoking some potentially dangerous code, catch errors and re-throw that error while filling additional contextual properties within the Error object
步骤 3:当调用一些有潜在危险的代码时,捕获错误并重新抛出该错误,同时在 Error 对象中填充额外的上下文属性
Step4: You must catch the exception during the request handling. This is easier if you use some leading promises library (BlueBird is great) which allows you to catch async errors. If you can't use promises than any built-in NODE library will return errors in callback.
Step4:您必须在请求处理期间捕获异常。如果您使用一些领先的 Promise 库(BlueBird 很棒),这会更容易,它允许您捕获异步错误。如果您不能使用承诺,那么任何内置的 NODE 库都会在回调中返回错误。
Step5: Now that your error is caught and contains descriptive information about what happens, you only need to map it to meaningful HTTP response. The nice part here is that you may have a centralized, single error handler that gets all the errors and map these to HTTP response:
第 5 步:现在您的错误已被捕获并包含有关发生情况的描述性信息,您只需要将其映射到有意义的 HTTP 响应。这里好的部分是你可能有一个集中的、单一的错误处理程序,它获取所有错误并将这些错误映射到 HTTP 响应:
//this specific example is using Express framework
res.status(getErrorHTTPCode(error))
function getErrorHTTPCode(error)
{
if(error.errorCode == commonErrors.InvalidInput)
return 400;
else if...
}
You may other related best practices here
回答by Jesse Dailey
Two things have really helped me solve this problem in my code.
在我的代码中,有两件事确实帮助我解决了这个问题。
- The 'longjohn' module, which lets you see the full stack trace (across multiple asyncronous callbacks).
A simple closure technique to keep exceptions within the standard
callback(err, data)
idiom (shown here in CoffeeScript).ferry_errors = (callback, f) -> return (a...) -> try f(a...) catch err callback(err)
- 'longjohn' 模块,可让您查看完整的堆栈跟踪(跨多个异步回调)。
一种简单的闭包技术,用于将异常保留在标准
callback(err, data)
习惯用法中(此处以 CoffeeScript 显示)。ferry_errors = (callback, f) -> return (a...) -> try f(a...) catch err callback(err)
Now you can wrap unsafe code, and your callbacks all handle errors the same way: by checking the error argument.
现在您可以包装不安全的代码,并且您的回调都以相同的方式处理错误:通过检查 error 参数。
回答by Lucio M. Tato
I've recently created a simple abstraction named WaitFor to call async functions in sync mode (based on Fibers): https://github.com/luciotato/waitfor
我最近创建了一个名为 WaitFor 的简单抽象,以在同步模式下调用异步函数(基于 Fibers):https: //github.com/luciotato/waitfor
It's too new to be "rock solid".
“坚如磐石”太新了。
using wait.foryou can use async function as if they were sync, without blocking node's event loop. It's almost the same you're used to:
使用wait.for您可以像同步一样使用异步函数,而不会阻塞节点的事件循环。这几乎和你习惯的一样:
var wait=require('wait.for');
function handleRequest(request, response) {
//launch fiber, keep node spinning
wait.launchFiber(handleinFiber,request, response);
}
function handleInFiber(request, response) {
try {
if (request.url=="whatever")
handleWhateverRequest(request, response);
else
throw new Error("404 not found");
} catch (e) {
response.writeHead(500, {'Content-Type': 'text/plain'});
response.end("Server error: "+e.message);
}
}
function handleWhateverRequest(request, response, callback) {
if (something)
throw new Error("something bad happened");
Response.end("OK");
}
Since you're in a fiber, you can program sequentially, "blocking the fiber", but not node's event loop.
由于您在光纤中,因此可以按顺序编程,“阻塞光纤”,但不能对节点的事件循环进行编程。
The other example:
另一个例子:
var sys = require('sys'),
fs = require('fs'),
wait = require('wait.for');
require("http").createServer( function(req,res){
wait.launchFiber(handleRequest,req,res) //handle in a fiber
).listen(8124);
function handleRequest(request, response) {
try {
var fd=wait.for(fs.open,"/proc/cpuinfo", "r");
console.log("File open.");
var buffer = new require('buffer').Buffer(10);
var bytesRead=wait.for(fs.read,fd, buffer, 0, 10, null);
buffer.dontTryThisAtHome(); // causes exception
response.end(buffer);
}
catch(err) {
response.end('ERROR: '+err.message);
}
}
As you can see, I used wait.forto call node's async functions in sync mode, without (visible) callbacks, so I can have all the code inside one try-catch block.
如您所见,我使用wait.for在同步模式下调用节点的异步函数,没有(可见的)回调,因此我可以将所有代码放在一个 try-catch 块中。
wait.forwill throw an exception if any of the async functions returns err!==null
如果任何异步函数返回 err!==null,wait.for将抛出异常
more info at https://github.com/luciotato/waitfor