Dart:让流中抛出的异常传播并在调用者中捕获它

Posted

技术标签:

【中文标题】Dart:让流中抛出的异常传播并在调用者中捕获它【英文标题】:Dart: Let an exception thrown inside a stream propagate and catch it in the caller 【发布时间】:2021-07-15 11:19:01 【问题描述】:

我希望在流内引发异常,以便由调用者在外部捕获和处理。

如果我尝试使用标准的 try-catch 块,如下所示,它确实有效:我得到 Uncaught Error: Exception

通过在 DartPad 中运行下面的 sn-p 代码

void main() async 
  await for (var i in mainStream()) 
    print(i);
  


Stream<int> subStreamA() async* 
  for (var i in List.generate(10, (i) => i)) 
    if (i == 2) 
      throw Exception();
    
    yield i;
  

Stream<int> subStreamB() async* 
  for (var i in List.generate(10, (i) => i + 10)) 
    yield i;
  


Stream<int> mainStream() async* 
  try 
    yield* subStreamA();
    yield* subStreamB();
   catch (e) 
    print('CAUGHT! :)');
    yield -1;
  



结果是:

0
1
Uncaught Error: Exception

我希望捕获异常,导致:

0
1
CAUGHT! :)
-1

我想到了一些“解决方案”,但它们有点可怕:(比如......

Stream<int> mainStream() async* 
  try 
    Exception? err;
    yield* subStreamA().handleError((e) 
      err = e;
    );
    if (err != null) throw err!;
    yield* subStreamB().handleError((e) 
      err = e;
    );
    if (err != null) throw err!;
   catch (e) 
    print('CAUGHT! :)');
    yield -1;
  

是否有一个干净的解决方案利用正常的 try-catch 而不使用肮脏的技巧?

【问题讨论】:

【参考方案1】:

yield*async* 标记方法的行为是 Dart 语言设计的一部分,其中描述了这一点:

监听o 流,创建订阅s,并为每个事件 x,或错误e 与堆栈跟踪ts

...

否则,xet 将添加到与 m 关联的流中 按照它们在o 中出现的顺序。

https://dart.dev/guides/language/specifications/DartLangSpec-v2.10.pdf

因此,简单来说,这意味着当我们使用 yield* 时,我们正在转发来自 Stream 的事件和错误(使用堆栈跟踪)。

如果要捕获异常,我认为最简单的解决方案是将yield* 的使用重写为await for 循环,如下所示:

Stream<int> mainStream() async* 
  try 
    await for (final event in subStreamA()) 
      yield event;
    
    await for (final event in subStreamB()) 
      yield event;
    
   catch (e) 
    print('CAUGHT! :)');
    yield -1;
  

通过此更改,我得到以下输出:

0
1
CAUGHT! :)
-1

【讨论】:

谢谢!这比我想到的所有解决方案都要干净 @gianmarcocalbi 更新了答案。该行为是 Dart 语言规范的一部分。 @gianmarcocalbi 如果您没有任何其他问题并且您觉得我的回答令人满意,请接受该回答。 :)

以上是关于Dart:让流中抛出的异常传播并在调用者中捕获它的主要内容,如果未能解决你的问题,请参考以下文章

捕获在不同线程中抛出的异常

捕获 AuthenticationProvider 中抛出的异常

是否可以捕获 JavaScript 异步回调中抛出的异常?

如何捕获 node_modules 中抛出的异常

是否有可能捕获JavaScript异步回调中抛出的异常?

AppDomain.AssemblyLoad 事件捕获事件处理程序中引发的所有异常