多级异步代码中的Dart错误

请考虑以下代码

import 'dart:async';

Future main() async {
  try {
    print("trying");
    await doSomething();
    print("success");
  } catch (e) {
    print("caught");
  }
}

Future<int> doSomething() async {
  await doSomethingElse();
  return 5;
}

Future<int> doSomethingElse() async {
  throw new Exception();
}

运行时, doSomethingElse() 中抛出的异常会在 main() 中被捕获,并且一切都按预期工作 . 但是,编写 doSomething() 方法的人并没有意识到 doSomethingElse() 是异步的,而是编写了以下内容(请注意缺少 await ) .

Future<int> doSomething() async {
  doSomethingElse();
  return 5;
}

现在根本没有发现异常 . 相反,输出现在看起来像这样:

trying
success
Unhandled exception:
Uncaught Error: Exception
Stack Trace:
#0      doSomethingElse.<doSomethingElse_async_body> (file:///C:/code/test.dart:19:7)
#1      Future.Future.<anonymous closure> (dart:async/future.dart:118)
<snip>

发生的事情是 doSomething() 立即返回,然后在另一个上下文中, doSomethingElse() 正在抛出错误,立即停止所有执行 . 我知道对此的答案可能是"Well, don't do that then."但是我在调用(例如,如果它们是库的一部分) .

这种情况导致了几个相关的问题:

  • 作为 main() 的作者,有什么方法可以确定我对 doSomething() 的调用不会以未处理的异常结束?或者我依赖于 doSomething() 的作者来确保所有可能的异常都被处理或传播到返回的Future?有没有办法附加某种全局错误处理程序,可以从废弃的期货中捕获错误?

  • 作为 doSomething() 的作者,如果我不想在 doSomethingElse() 上等待(例如它写入日志,那么我既不需要输出也不需要担心处理错误) . 我有什么办法可以防止 doSomethingElse() 中的错误暂停程序而不是将每次调用包装成try / catch块(这可能很容易被忽略)?

  • 作为 doSomethingElse() 的作者,是否有一些我可以使用的模式允许我以等待Future完成的调用者自己处理异常的方式抛出异常,而不需要担心捕获异常的调用者#753689_t ?在这方面我最好的想法是返回一个特殊的对象,而不是抛出异常,但这会增加很多额外的瑕疵,并使该方法更难使用 .

注意:我在这里使用async / await语法,但问题应该与更严格的基于Future的构造同等相关(在 doSomething() 中返回一个新的Future而不是 .then() 中的 .then()

回答(2)

2 years ago

未捕获的异步错误将处理到当前区域的错误处理程序 . 您所看到的是根区域的错误处理程序,将错误报告为未捕获,这也会终止隔离 .

你想要的是为你的代码引入一个不同的错误处理程序,通过错误处理程序运行 runZoned

import "dart:async";
main() {
  runZoned(() async {
    try {
      print("trying");
      await doSomething();
      print("success");
    } catch (e) {
      print("caught");
    }
  }, onError: (e, s) {
    print("uncaught");
  });
}

2 years ago

Greg pointed in its comment类似,您可以使用Zones来捕获异步代码中的意外错误 .