提问人:Dan R 提问时间:9/16/2023 最后编辑:Dan R 更新时间:9/17/2023 访问量:52
处理标记为异步的 void Dart 函数中的错误
Handling errors in void Dart function marked async
问:
我正在设计一个 API,并希望处理用户错误地标记函数的情况。void
async
下面列出了简化代码:
void test(void Function() run) {
try {
for (var i = 0; i < 3; i++) {
print(i);
run();
}
} catch (e) {
print(e);
} finally {
print('in finally block.');
}
}
void errorThrower(String message) {
print('in errorThrower ');
throw message;
}
void main(List<String> args) {
test(() async{ // <-------- marked async by API user's mistake
print('in run ...');
errorThrower('error thrown in test');
});
}
如果未标记该函数,则程序输出符合预期,错误为
抛出、捕获、处理,然后执行块:async
finally
$ dart main.dart
0
in run ...
in errorThrower
error thrown in test
in finally block.
但是,如果标记了该函数,则控制台输出为:async
$ dart main.dart
0
in run ...
in errorThrower
1
in run ...
in errorThrower
2
in run ...
in errorThrower
in finally block.
Unhandled exception:
error thrown in test
#0 errorThrower (file:///home/dan/WORK/DartProjects/benchmark_runner/bin/throws_test.dart:16:3)
#1 main.<anonymous closure> (file:///main.dart:22:5)
#2 test (file:///main.dart:5:10)
#3 main (file:///main.dart:20:3)
#4 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:294:33)
#5 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:189:12)
这是怎么回事?我尝试了一个调试器,程序进入然后,然后循环继续。
这看起来像是一个未等待的未来错误,但我不知道如何捕获和处理它,因为无法等待函数。run()
errorThrower
void
有什么方法可以在不更改签名的情况下捕获和处理错误?run()
答:
看,我可以解释一下您的预期结果和实际结果的差异。
当你将一个函数标记为 时,这意味着该函数返回一种特殊类型的对象,称为 .A 表示可能完成也可能未完成的潜在操作。当您调用一个函数时,它会立即返回,并且函数内部的工作发生在 .这就是 Dart 能够执行 .async
Future
Future
asynchronous
async
background
asynchronous programming
在您的函数中,您将另一个称为 run 的函数作为参数。当你将 run 标记为 时,这意味着它正在返回一个 ,当你调用它时,函数执行不会暂停以等待 run 函数完成。相反,它立即与循环。test
async
Future<void>
without await
continues
这就是为什么您看到循环在不等待 run 函数完成的情况下运行的原因。它同时运行,并且它抛出的块不会被捕获到块中,因为块在运行函数完成之前已经完成。exceptions
try/catch
try/catch
下面的代码将导致您预期的答案:
import 'dart:async';
Future<void> test(Future<void> Function() run) async {
try {
for (var i = 0; i < 3; i++) {
print(i);
await run(); // Await the execution of the async function.
}
} catch (e) {
print(e);
} finally {
print('In finally block.');
}
}
Future<void> main(List<String> args) async {
await test(() async {
throw 'In void function erroneously marked async';
});
}
现在,想想它是如何给出预期的结果的!如果没有收到,请回复。
评论
run()
async
async
run()
async
await
这是怎么回事?
标记的函数会自动转换,以便返回值和抛出的对象包装在 s 中。失败的 s 必须通过向 Future.catchError
注册回调、在块内使用 using (这是注册回调的语法糖)或设置 Zone
错误处理程序(我认为这超出了本答案的范围)来处理。从概念上讲,你可以认为失败是在同步函数已经离开它的 - 块之后从 Dart 事件循环中抛出异常。async
Future
Future
await
Future
try
Future.catchError
Future
test
try
catch
有什么方法可以在不更改签名的情况下捕获和处理错误?
run()
T Function() 是 void Function()
的子类型(
可替代)。因此,在编译时,您无法阻止在预期 a 的地方传递 a。这也是为什么像这样的事情不能阻止异步回调(即使这几乎总是一个坏主意)并且需要 linter 的支持。Future<void> Function()
void Function()
Iterable.forEach
您可以添加运行时检查:
void test(void Function() run) {
if (run is Future<void> Function()) {
throw ArgumentError("test: Cannot be used with asynchronous callbacks.");
}
...
}
或者,如果您只想吞下错误:
void test(void Function() run) {
...
if (run is Future<void> Function()) {
run().catchError((e) => print(e));
} else {
run();
}
...
}
请注意,如果要等待完成(成功与否),则其本身需要异步,并且需要返回 .在这一点上,你不妨总是假设它可能是异步的,并且无条件地:test
run
test
Future
run
await
Future<void> test(FutureOr<void> Function() run) async {
try {
...
await run();
...
}
评论