Node.js 承诺和异步异常
Posted
技术标签:
【中文标题】Node.js 承诺和异步异常【英文标题】:Node.js promises and asynchronous exceptions 【发布时间】:2015-10-09 20:38:14 【问题描述】:我使用“promise”(但也尝试过“bluebird”)npm 模块大量使用 Promises 来包装异步代码。而且我对它不处理异步抛出并不感到惊讶:
var Promise = require("promise"); // also tried require("bluebird") here
function asyncThrow()
return new Promise(function(resolve, reject)
process.nextTick(function()
throw new Error("Not handled!");
)
);
asyncThrow()
.then(function()
console.log("resolved");
)
.catch(function()
console.log("rejected");
);
在此代码执行期间,node.js 存在未经处理的异常(我预计会出现这种行为)。
我也尝试过基于“域”的错误处理:
var Promise = require("promise"); // also tried require("bluebird") here
var domain = require("domain");
function asyncThrow()
return new Promise(function(resolve, reject)
var d = domain.create();
d.on("error", reject);
d.run(function()
process.nextTick(function()
throw new Error("Not handled!");
)
);
);
asyncThrow()
.then(function()
console.log("resolved");
,
function()
console.log("rejected");
)
.catch(function()
console.log("catch-rejected");
);
此代码行为要好得多,但正如预期的那样 - 调用了“拒绝”函数。
所以问题是:
-
如何在处理异步代码时强制“catch-reject”函数调用?
这种方法是否会显着降低性能?
您能否提出更好的方法来处理此类异常?
【问题讨论】:
为什么要用process.nextTick()
来抛出异常?您在第一个示例中保留了承诺范围。因此它没有被抓住。
@Sirko 这是对例如网络交互行为的模拟。当连接丢失时抛出异常。
您是否考虑过使用denodify()
也将您的异步调用转换为承诺?
@Sirko 正如我在 denodify 的源代码中看到的那样,它没有任何魔力 - 它只是一个调用包装器。
Promise 库只能捕获在它直接调用的函数或它们直接调用的函数中抛出的异常。没有 Promise 库可以捕获在其他异步回调中引发的异常。因此,当nextTick()
回调中抛出异常时,promise 库无法捕获它。您必须自己捕获它或承诺该特定函数(它将回调替换为 promise 库管理的回调,因此可以捕获异常)。这只是 javascript 工作方式的现实/限制,也是需要理解的一个重要方面。
【参考方案1】:
您可以使用Promise.denodeify(fn) 来实现此目的。
var Promise = require("promise");
function asyncThrow()
return new Promise(function(resolve, reject)
// Denodify the process.nextTick function
var nextTick = Promise.denodeify(process.nextTick)
// Utilize nextTick and return the promise
return nextTick.then(function()
throw new Error("Not handled!");
)
);
asyncThrow()
.then(function()
console.log("resolved");
)
.catch(function()
console.log("rejected");
);
这将导致调用.catch()
函数。
【讨论】:
这不是解决方案,因为我们也使用第三方调用。我们无法控制它们。例如,我们使用使用网络堆栈的库(例如 sip.js),我们不能强制该库使用 Promise,但喜欢将它们包装起来。 啊,我明白了 - 那么域方法可能是更好的解决方案。 如果有办法调用catch-rejected,域方法就可以了。性能也是未知的(我还没有对此解决方案进行性能测试)。以上是关于Node.js 承诺和异步异常的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 promise 将数组从我的模块返回到我的 API?异步函数和承诺 - Node.js