超时的用户输入无法按预期工作
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 并调用 prompt
。 prompt
附加到 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在prompt
return秒时更新,不过没关系,因为你已经不看了。 if $str eq 'q'
条件将检查 $str
数字 2,因为这是在当前循环中声明的变量。
第二次调用 prompt
然后立即要求输入,如果您在超时到期前键入 q
,它会更新它关闭的 $str
的版本,$str
数字 2。因为这是你的条件正在检查的那个,循环终止。
每次超时都会启动一个新的 prompt
而不会终止旧的,这意味着用户键入的输入永远不会附加到您正在检查的同一个 $str
。即使您检查了原始变量,对 prompt
的后续调用仍然会发生,并且即使在执行离开块后也会继续提示。
因为 prompt
没有办法指定超时,Raku 也没有办法 "kill" 预定 Promises
,我不认为你能用 prompt
.
解决这个问题
为什么在超时后循环不再像以前那样工作(不再打印用户输入)?
#!/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 并调用 prompt
。 prompt
附加到 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在prompt
return秒时更新,不过没关系,因为你已经不看了。 if $str eq 'q'
条件将检查 $str
数字 2,因为这是在当前循环中声明的变量。
第二次调用 prompt
然后立即要求输入,如果您在超时到期前键入 q
,它会更新它关闭的 $str
的版本,$str
数字 2。因为这是你的条件正在检查的那个,循环终止。
每次超时都会启动一个新的 prompt
而不会终止旧的,这意味着用户键入的输入永远不会附加到您正在检查的同一个 $str
。即使您检查了原始变量,对 prompt
的后续调用仍然会发生,并且即使在执行离开块后也会继续提示。
因为 prompt
没有办法指定超时,Raku 也没有办法 "kill" 预定 Promises
,我不认为你能用 prompt
.