超时的用户输入无法按预期工作

User input with timeout doesn't work as hoped

为什么在超时后循环不再像以前那样工作(不再打印用户输入)?

#!/usr/bin/env raku

loop {
    my $str;
    my $timeout = Promise.in( 5 ).then({
        $str = 'Timeout';
    });
    my $user = Promise.start({
        $str = prompt '>';
    });
    await Promise.anyof( $timeout, $user );
    if $str eq 'q' {
        last;
    }
    say "[$str]";
}

此代码中的逻辑问题是超时承诺 在 5 秒后触发,即使有人在上一次迭代中输入了某些内容。因此它会在看似随机的时间设置 $str

有一个简单的解决方案:只要确保您在超时代码中 not assign $str 如果已经设置:

$str //= 'Timeout';

对于这个例子,这并不重要,但通常您不希望代码随意执行,因此最好实际停用 Promise。不幸的是,您不能使用 Promise 界面来做到这一点。但是 Promise.in 方法实际上是 ThreadPoolScheduler.cue 方法的包装器, return 一个 Cancellation 对象(https://docs.raku.org/routine/cue).

这是因为您正在与对 prompt 的错误调用对话,这是在一个已关闭不同变量 $str 的承诺中。第二次及以后调用 prompt 块,等待第一次调用完成。但是接收第一次调用值的 $str 超出了范围,所以什么也没有发生。

这听起来很奇怪,但这里有一个实验,你可以 运行 帮助你的直觉,同时我会更全面地剖析它:运行 脚本,等待超时,然后输入 q 快速连续两次。脚本在第二个之后退出。为什么?

在第一个循环中,我们声明一个变量 $str,我将其称为“$str 数字 1”,并创建一个 Promise 关闭 $str 号码 1 并调用 promptprompt 附加到 STDIN 并且在看到换行符之前不会 return。当超时到期时,对 prompt 的调用不会中断。还是运行宁。还在等。它所附加的承诺(我们称之为 $user 承诺 1)仍然有效,即使变量 $user 即将超出范围。

在第二个循环中,我们声明一个新变量$str$str number 2"),创建一个Promise 关闭it,然后再次调用 prompt。但是对 prompt 的另一个调用仍在使用 STDIN,因此新调用会阻塞并等待 STDIN 可用。如果你现在输入一些东西,它会被最初调用 prompt 看到,它附加到 $user promise 1 并关闭 $str number 1.

$str数1在promptreturn秒时更新,不过没关系,因为你已经不看了。 if $str eq 'q' 条件将检查 $str 数字 2,因为这是在当前循环中声明的变量。

第二次调用 prompt 然后立即要求输入,如果您在超时到期前键入 q,它会更新它关闭的 $str 的版本,$str 数字 2。因为这是你的条件正在检查的那个,循环终止。

每次超时都会启动一个新的 prompt 而不会终止旧的,这意味着用户键入的输入永远不会附加到您正在检查的同一个 $str。即使您检查了原始变量,对 prompt 的后续调用仍然会发生,并且即使在执行离开块后也会继续提示。

因为 prompt 没有办法指定超时,Raku 也没有办法 "kill" 预定 Promises,我不认为你能用 prompt.

解决这个问题