我可以在不使用 await 的情况下从异步中捕获错误吗?
Posted
技术标签:
【中文标题】我可以在不使用 await 的情况下从异步中捕获错误吗?【英文标题】:Can I catch an error from async without using await? 【发布时间】:2015-08-19 10:16:17 【问题描述】:是否可以捕获来自非等待异步调用的错误,将其发送到原始封装 try/catch,或引发未捕获的异常?
这是我的意思的一个例子:
async function fn1()
console.log('executing fn1');
async function fn2()
console.log('executing fn2');
throw new Error('from fn2');
async function test()
try
await fn1();
fn2();
catch(e)
console.log('caught error inside test:', e);
test();
在这种情况下,fn2
抛出的错误会被默默吞噬,绝对不会被原来的try/catch
捕捉到。我相信这是预期的行为,因为 fn2
很可能会被推到事件循环中以在将来的某个时间完成,而 test
并不关心它何时完成(这是有意的)。
除了将try/catch
放在fn2
内部并执行诸如发出错误之类的操作之外,有什么方法可以确保错误不会被这样的结构意外吞没?我什至会在不知道如何捕获它的情况下解决一个未捕获的错误,我认为——我不希望抛出的错误是我正在编写的典型程序流程,但是吞下错误会使调试相对烦人。
旁注,我正在使用 Babel 使用 babel-runtime 转换转换代码,并使用节点执行它。
【问题讨论】:
我不确定你想达到什么目的,但有理由不使用承诺吗? 使用支持未处理拒绝检测的承诺库。 汤姆,不是真的,不。我故意在 promises 上使用 async/await 来看看此时可以用 es7 语法做什么,这是我在玩它时遇到的一个问题。 Bergi,如果目前没有其他选择(我怀疑可能是这种情况),我肯定会回到那个位置。 @dvlsg 请注意,使用 Babel,您有一个 bluebirdCoroutines 转换,可让您将 bluebird Promise 与本机 async/await 一起使用。 【参考方案1】:处理未处理的被拒绝的原生 Promise(并且 async/await 使用原生 Promise)是 V8 现在支持的一项功能。它在最新的 Chrome 中用于在未处理拒绝的 Promise 时输出调试信息;在the Babel REPL 尝试以下操作:
async function executor()
console.log("execute");
async function doStuff()
console.log("do stuff");
throw new Error("omg");
function handleException()
console.error("Exception handled");
(async function()
try
await executor();
doStuff();
catch(e)
handleException();
)()
您会看到,即使来自doStuff()
的异常丢失(因为我们调用它时没有使用await
),Chrome 也会记录一个被拒绝的承诺未处理到控制台:
这在 Node.js 4.0+ 中也可用,但需要收听 a special unhandledRejection
event:
process.on('unhandledRejection', function(reason, p)
console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason);
// application specific logging, throwing an error, or other logic here
);
【讨论】:
啊,完美。感谢 github 问题的链接,我会密切关注这些问题,在测试我正在做的事情时,我会暂时改用 io.js。 啊,最后一个警告——我必须添加process.on('unhandledRejection', err => throw err; );
才能让它在 io.js 中按预期工作,基于他们的 unit tests。仍然有点 hacky,但我收到了一个错误,其中包含(某种)有用的堆栈跟踪,这种方式。
玩这个,似乎无法捕获从没有await
调用的async
函数引发的错误。这是正确的看待它的方式吗?
@dvlsg 做process.on('unhandledRejection', err => throw err; );
绝对没问题。另请注意,由于 NodeJS 和 io.js 正在合并,因此可以安全地使用此功能,因为它将在 Node 中收敛。
作为跟进,现在 Node 4.0 已经发布,我可以确认 process.on('unhandledRejection', err => /* ... stuff */ );
在 iojs 合并后按预期工作,并且仍然有必要防止意外吞下未捕获的拒绝(而不是Chrome 中的行为)。【参考方案2】:
如果您熟悉promises,请使用它们。如果没有,你可以试试这个例子,让你的代码更加异步:)
function fn1(callback)
console.log('executing fn1');
callback(status: true);
function fn2(callback)
console.log('executing fn2');
callback(status: false);
function test()
fn1(function(result)
console.log('fn1 executed with status ' + result.status);
);
fn2(function(result)
console.log('fn2 executed with status ' + result.status);
if (result.status == false)
console.log('error in fn2');
);
test();
【讨论】:
感谢您的回复。不过,在这种情况下,我有意使用 ES7 async/await。我知道,我很固执,而且我使用的功能甚至还没有被完全接受,但它们显着提高了我的实际代码的语法和可读性(它涉及async
函数中的长/无限 while 循环await
在循环内)。以上是关于我可以在不使用 await 的情况下从异步中捕获错误吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不点击的情况下从 Chrome 自定义标签中捕获 URL?
如何在不影响 Node.JS 应用程序的情况下从后台异步发送大量电子邮件?
如何在不返回 Promise 的情况下从对象的`get()` 获取异步数据