从单独的线程(例如,开始块、Proc::Async 或包含这些的子程序)传播错误的最佳方法是什么。简单地将在 try/CATCH 块中分出新线程的代码包装起来是行不通的,并且使用 await 仅根据子例程的返回值起作用(即,子返回 self 不适用于 await 方法)。
使用await.
例如,替换代码中的这三行:
foo;
bar;
baz;
Run Code Online (Sandbox Code Playgroud)
和:
await foo, bar, baz;
Run Code Online (Sandbox Code Playgroud)
从理论上讲,该代码应该死:
从该语言的 6.d 版本开始,接收器上下文中使用的 start 语句前缀将自动附加异常处理程序。如果在给定的代码中发生异常,它将被打印出来,然后程序将退出,就像它在不涉及任何开始语句前缀的情况下抛出一样。
use v6.c;
start { die }; sleep ?; say "hello"; # OUTPUT: «hello?»
use v6.d;
start { die }; sleep ?; say "hello";
# OUTPUT:
# Unhandled exception in code scheduled on thread 4
# Died
# in block at -e line 1
Run Code Online (Sandbox Code Playgroud)
在这种情况下,这是一个奇怪的情况,因为您没有沉没承诺(您正在返回它),但最终您沉没了它,因为您是在无效上下文中运行它。
相同的文档为您提供了解决方案:不要沉没上下文:
# Don't sink it:
my $ = start { die }; sleep ?; say "hello"; # OUTPUT: «hello?»
# Catch yourself:
start { die; CATCH { default { say "caught" } } };
sleep ?;
say "hello";
Run Code Online (Sandbox Code Playgroud)
由于您的程序没有死,我会说您处于第二种情况。出于某种原因,它没有沉没。但不管是什么情况,解决方案都是一样的:你需要在同一个代码块中捕获异常。
解决方案:await承诺(不会沉没)或将其分配给某个变量,这样周围的代码也会死亡。但是响应您的 OP,不,您无法从另一个线程捕获异常,就像您无法从另一个块捕获异常一样。
遵循 Go 中使用通道将错误传递出 go 例程的约定,我发现在 Raku 中也可以使用相同的方法。可以使用 Channel 将错误从异步代码中发送出去,由主线程处理。
例子:
my $errors = Channel.new;
my $err-supply = $errors.Supply;
$err-supply.tap(-> $e {say "handle error: $e"});
start {
die "something went horribly wrong";
CATCH {
default {
$errors.send($_);
}
}
}
sleep 1;
Run Code Online (Sandbox Code Playgroud)