见下面的例子:
import 'dart:async';
Future<void> main(List<String> arguments) async {
try {
await someMethod();
} catch (e, st) {
print("error: ${e}");
print("st : ${st}");
}
}
Future<void> someMethod() async {
final completer = Completer.sync();
_scheduleCompletion(completer);
await completer.future;
}
Future<void> _scheduleCompletion(Completer completer) async {
Future.delayed(Duration(milliseconds: 1), () {
try {
[][0]; // simulating error
completer.complete();
} catch (e, st) {
completer.completeError("error occured", st);
}
});
}
我收到以下产出:
#0 List.[] (dart:core-patch/growable_array.dart:254:60)
#1 _scheduleCompletion.<anonymous closure> (file:///home/work/stuff/projects/dart-async-exceptions/bin/dart_async_exceptions.dart:26:9)
#2 new Future.delayed.<anonymous closure> (dart:async/future.dart:315:39)
#3 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
#4 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395:19)
#5 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426:5)
#6 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
如您所见,堆栈跟踪不允许您查看导致错误的步骤。我所期望的是:
[anonymous closure, where error actually happens]
[someMethod]
[main]
而我只看到最后一部分。
我知道有一个叫做跟踪的包,它允许你“用Chain.capture包装你的整个程序,你会很高兴的”。不幸的是,我的实际情况并不是那么简单,我不能简单地用"Chain.capture“来”包装我的程序“。不使用"Chain.capture“的另一个原因是,在某些情况下,它的onError处理程序会变得更糟,而不是简单的try/catch块。
此外,我想了解为什么会发生这种情况,以及我应该做些什么来修复我的程序,这样就不会丢失部分有价值的跟踪。
更新(基于Irn的回答):
让我们重新编写someMethod,而不是使用completer,而是调用另一个异步方法:
Future<void> someMethod() async {
await someOtherMethod();
}
Future<void> someOtherMethod() async {
throw "another error";
}
在这种情况下,我将有以下跟踪:
#0 someOtherMethod (file:///home/work/stuff/projects/dart-async-exceptions/bin/dart_async_exceptions_original.dart:24:3)
<asynchronous suspension>
#1 someMethod (file:///home/work/stuff/projects/dart-async-exceptions/bin/dart_async_exceptions_original.dart:19:3)
<asynchronous suspension>
#2 main (file:///home/work/stuff/projects/dart-async-exceptions/bin/dart_async_exceptions_original.dart:5:5)
<asynchronous suspension>
正如您所看到的,它包含所有调用--主-> someMethod -> someOtherMethod,这使您可以缩小问题范围。正如我所理解的,在这种情况下,当我们在事件循环上调度调用时,我们也应该失去所有的东西。但我们认为一切都如预期的那样运作。为什么会这样呢?
About stack_trace package:我很想使用它,但我做了一些小实验,看起来有时它提供的跟踪比try/catch提供的更糟。但这是另一个问题的主题;)
发布于 2021-06-24 08:31:27
简单的回答是,您无法跟踪不存在的堆栈。
Dart的异步代码是基于事件循环的。当您执行await someFuture
时,您的函数实际上使用Future.then
设置了对该未来的回调,然后它返回。这会将堆栈展开,导致设置回调,并最终使堆栈返回到事件循环。(只有在计算await
时,someFuture
表达式才会立即被调用,如果在执行异步操作之前设法抛出,则会在await
返回之前得到包含堆栈的堆栈跟踪)。
稍后,事件循环触发并调用回调,然后异步计算将继续进行。
原来的堆栈在那时不再存在,这就是为什么您看到堆栈跟踪返回到事件循环事件(这里是一个定时器,VM通过在特定时间发送端口事件来实现)。您看到的堆栈是唯一存在于您所要求的点上的堆栈。
可以使用package:stack_trace
在调度回调的点捕获堆栈跟踪,然后堆栈跟踪包将记住该堆栈跟踪,直到回调运行为止,并尝试将其与回调期间报告(或捕获)的堆栈跟踪结合起来。由于您进行了大量回调,但通常不需要看到那么多堆栈跟踪,因此在每次回调时捕获堆栈都会对性能产生重大影响。
https://stackoverflow.com/questions/68101983
复制相似问题