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

Posted

技术标签:

【中文标题】使用 node.js 处理 Javascript 异步异常【英文标题】:Javascript Asynchronous Exception Handling with node.js 【发布时间】:2012-12-27 10:23:07 【问题描述】:

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

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

此服务器支持(表示喜欢)路由。 例如,我的代码如下所示:

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

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

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 可以帮助我(关于 throw 在 promise 中的内容),在这种情况下 promise 可以帮助我吗?

【问题讨论】:

AFAIK,在诸如域之类的东西之外不可能从异步代码中捕获抛出的异常(这听起来很像“将所有抛出的错误从一个函数绑定到一个特定的处理函数”);另一种方法是使用标准的 Node.js 回调样式,其中第一个参数是错误而不是抛出(实际上这就是这种模式如此普遍的原因)。 我必须能够使用不是我自己编写的代码。理想的解决方案是 functionName.exceptionHandler = someFunction(exception) 之类的。 完全理解——但我不确定你会很容易理解。您必须拦截调用(这是 Domains 尝试对内置事件发射器执行的操作)。 (也许 javascript 的一些理论上的扩展——想想 CoffeeScript——可以为你生成必要的代码。)事实上,异步代码中的throwing 是不好的形式,例如它不能是帮助(可能是 JSON.parse 失败等),uncaughtException 事件是您保存应用程序的门票(尽管库在自己的代码中使用 catch 肯定会更好)。 @BrandonTilley 感谢您对我自己的回答的反馈 @BenjaminGruenbaum 你能回答这个***.com/questions/43069522/… 【参考方案1】:

警告:我不推荐使用域的原始答案,域将来会被弃用,我在编写原始答案时很开心,但我不再认为它太相关了。相反-我建议使用具有更好错误处理能力的事件发射器和承诺-下面是带有承诺的示例。这里使用的 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 - npm install atry 上发布的模块中实现了你的想法 @RushPL 非常好!公平地说 - NodeJS 核心在抛出时不会很好地自行清理,所以这应该主要用于用户态代码。 Promise 有一个 throw 保证(你可以把它们扔进去!),promisified 代码是 throw 安全的,自从 Bluebird 出现以来,它们和回调一样快,所以目前我在我的代码中使用它们。它的工作方式也非常相似Promise.try(function() return Promise.delay(30).then(function() throw new Error("Hi"););).catch(function(err) console.log(err); ); 看起来不错的库,在设计未来的 API 时值得一试!我认为虽然atry 在处理现有 API 或 Node API 时仍然可能有用 - 只需要一个关于可能的资源泄漏的巨大警告,所以我将添加对这个 ***.com/questions/15825752/… 的引用

以上是关于使用 node.js 处理 Javascript 异步异常的主要内容,如果未能解决你的问题,请参考以下文章

Node.js的事件处理机制

Solana - 如何从 JavaScript / Node.js 中的本地密钥对获取帐户?

Propel: 由Node.js之父创建的JavaScript科学计算库

Node.Js学习day01初识 Node.js 与内置模块

Node.Js学习day01初识 Node.js 与内置模块

JavaScript使用事件循环在调用堆栈