假设我在一个线程中进行简单的套接字侦听,并且我想保留另一个线程,直到端口打开并且套接字实际上正在侦听。哪一个是正确的:
do
? do
的文档显示为 The simplest way to run a block
,但 whenever
的主体在某些连接之前实际上并未运行。my $p = Promise.new;
start {
react {
$p.keep: do whenever IO::Socket::Async.listen( "127.0.0.1", 4528 ) -> $c { ... }
}
};
await $p;
whenever
块之后,该块中使用的电源可以访问调度程序并且已经开始工作?my $p = Promise.new;
start {
react {
whenever IO::Socket::Async.listen( "127.0.0.1", 4528 ) -> $c { ... }
$p.keep;
}
};
await $p;
INIT
的文档有点不清楚。它说它应该在运行时尽快运行。所以它看起来是保证块初始化的完美候选者。但为什么它无法访问已定义的 Promise?my $p = Promise.new;
start {
react {
whenever IO::Socket::Async.listen( "127.0.0.1", 4528 ) -> $c {
INIT $p.keep; # ERROR, variable undefined
}
}
};
await $p;
以上都不是。 :-)
INIT
块在模块加载后立即运行,并在程序的生命周期中运行一次。它通常用于执行与整个模块相关的初始化工作。因此它会在 start
或 react
发生之前运行。
选项 2 更接近,但还不够。发出开始监听的请求后,
Promise
将被保留,但这是一个异步操作,因此存在竞争。
选项 1 是错误的,但朝着一个有趣的方向发展。
whenever
构造建立订阅,并计算出表示订阅的 Tap
对象。通常人们不关心这个对象,因为 react
/supply
构造完成了所有订阅管理。然而,某些类型的 Supply
返回比其他类型更有趣的 Tap
对象。 IO::Async::Socket.listen
方法返回 IO::Socket::Async::ListenSocket。
其中的方法是
socket-port
,这是一个与服务器正在侦听的端口保持一致的Promise
。最常见的用途是当您请求服务器侦听任何空闲端口并想要发现选择了哪个端口时,但您也可以使用它来查明服务器何时确实在侦听。
因此解决方案是:
my $p = Promise.new;
start {
react {
my $server = do whenever IO::Socket::Async.listen( "127.0.0.1", 4528 ) -> $c { ... }
whenever $sever.socket-port {
$p.keep;
}
}
};
await $p;