我可以在不使用 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 的情况下从异步中捕获错误吗?的主要内容,如果未能解决你的问题,请参考以下文章

Android:如何在不拍照的情况下从相机捕获文本?

如何在不点击的情况下从 Chrome 自定义标签中捕获 URL?

如何在不影响 Node.JS 应用程序的情况下从后台异步发送大量电子邮件?

如何在不返回 Promise 的情况下从对象的`get()` 获取异步数据

是否可以在没有 async/await 的情况下从 Promise 返回已解析的值?

问题:机架被占用。是不是可以在不使用流程图的情况下从机架系统中删除代理?