使用 node.js 进行 Javascript 异步异常处理

IT技术 javascript node.js asynchronous exception-handling promise
2021-03-04 00:19:19

我目前正在开发一个 node.js 应用程序,但遇到了常见的异步代码问题。

我正在 Node 的 HTTP module之上实现一个服务服务器。

该服务器支持(express like)路由。例如,我的代码如下所示:

server.any("/someRoute",function(req,resp){
    resp.end("this text is sent to clients via http")
});

服务器需要能够承受故障,当传递给 any 的函数出现问题时,我不想让整个服务器崩溃。当我编写如下代码时会出现问题:

server.any("/someRoute",function(req,resp){
    setTimeout(function(){
        throw new Error("This won't get caught");
    },100);
});

我不知道我怎么可能在这里捕获错误。我不想因为一个服务器端故障而使服务器崩溃,而是想提供 500 个服务。

我能想出的唯一解决方案真的没有表现力。我只是想出了process.on("uncaughtException",callback)使用节点 0.8 的类似代码Domains(这是一个部分补救措施,但域目前有问题,这仍然不是很具有表现力,因为我最终不得不为每个句柄创建一个域)。

我想要完成的是将throw操作从函数绑定到作用域,理想的解决方案是将所有抛出的错误从函数绑定到特定的处理函数。

这可能吗?在这种情况下处理错误的最佳做法是什么?

我想强调的是,它应该能够在出现错误请求后继续服务请求,并且在每个请求上重新启动服务器或为每个处理程序创建域并捕获它们未捕获的异常对我来说似乎是一个坏主意。另外 - 我听说Promise可能会帮助我(throwPromise中的东西),在这种情况下Promise可以帮助我吗?

1个回答

警告:我不推荐使用域的原始答案,域将来会被弃用,我写原始答案很有趣,但我不再认为它太相关了。相反 - 我建议使用具有更好错误处理能力的事件发射器和Promise - 这是下面的例子,而不是Promise。这里使用的Promise是Bluebird

Promise.try(function(){ 
    throw new Error("Something");
}).catch(function(err){
    console.log(err.message); // logs "Something"
});

超时(请注意,我们必须返回 Promise.delay):

Promise.try(function() {
    return Promise.delay(1000).then(function(){
        throw new Error("something");
    });
}).catch(function(err){
    console.log("caught "+err.message);
});

使用通用的 NodeJS 函数:

var fs = Promise.promisifyAll("fs"); // creates readFileAsync that returns promise
fs.readFileAsync("myfile.txt").then(function(content){
    console.log(content.toString()); // logs the file's contents
    // can throw here and it'll catch it
}).catch(function(err){
    console.log(err); // log any error from the `then` or the readFile operation
});

这种方法既快速又安全,我建议在以下答案上方使用它,该答案使用可能不会留下来的域。


我最终使用了域,我创建了我调用的以下文件mistake.js,其中包含以下代码:

var domain=require("domain");
module.exports = function(func){
    var dom = domain.create();
    return { "catch" :function(errHandle){
        var args = arguments;
        dom.on("error",function(err){
            return errHandle(err);
        }).run(function(){
            func.call(null, args);
        });
        return this;
    };
};

以下是一些示例用法:

var atry = require("./mistake.js");

atry(function() {
    setTimeout(function(){
        throw "something";
    },1000);
}).catch(function(err){
    console.log("caught "+err);
});

它也像同步代码的普通捕获一样工作

atry(function() {
    throw "something";
}).catch(function(err){
    console.log("caught "+err);
});

我将不胜感激有关解决方案的一些反馈

附带说明一下,在 v 0.8 中,显然当您在域中捕获异常时,它仍然冒泡到process.on("uncaughtException"). 我处理这在我process.on("uncaughtException")

 if (typeof e !== "object" || !e["domain_thrown"]) {

但是,文档建议反对process.on("uncaughtException")任何方式

我已经在 npm github.com/CodeCharmLtd/atry上发布的module中实现了您的想法-npm install atry
2021-04-21 00:19:19
@RushPL 非常好!公平地说 - NodeJS 核心在抛出时并没有很好地清理自己,所以这应该主要用于用户空间代码。Promises 有一个 throw 保证(你可以把它们扔进去!),promisified 代码是安全的,自从Bluebird出现以来,它们和回调一样快,所以目前我在我的代码中使用它们。它的工作原理也非常相似Promise.try(function(){ return Promise.delay(30).then(function(){ throw new Error("Hi");});}).catch(function(err){ console.log(err); });
2021-04-22 00:19:19
我认为这很好——将所有域创建保留在自己的位置。
2021-04-30 00:19:19
非常好,我喜欢你的想法!
2021-05-15 00:19:19
看起来是一个不错的库,值得在设计未来的 API 时检查!我认为尽管atry在处理现有 API 或 Node API 时仍然可能有用 - 只需要一个关于可能的资源泄漏的巨大警告,所以我将添加对此stackoverflow.com/questions/15825752/...
2021-05-19 00:19:19