Perl中Promises与AnyEvent结合时后续then回调未执行问题求助
问题:AnyEvent与Promises结合时,条件满足后后续then回调未执行
尝试编写一个基于AnyEvent和Promises的同步逻辑:设置5秒定时器将$inputs设为'next',my_p_await函数每隔3秒检查条件是否满足。运行后发现,当条件满足($inputs为'next')后,后续的then回调并未执行,看不到****>==> Time: 0: 的输出,但AnyEvent的15秒定时器正常运行。
原代码:
#!/usr/bin/perl use strict; use warnings; use AnyEvent; use AE; use Time::Piece; use Promises backend =>['AnyEvent'], 'deferred'; use feature q(say); use Data::Dumper; $| = 1; my $inputs = 'starting'; my $w2; $w2 = AE::timer 5, 0, sub { say '***> AE::io timer : pass 5sec : ', Time::Piece->new(AE::now, "%s")->strftime("%Y-%m-%d %H:%M:%S"); say '***> making : inputs = next'; $inputs = 'next'; }; my $w3; $w3 = AE::timer 15, 15, sub { say 'AE::io timer : passed 15 sec : ', Time::Piece->new(AE::now, "%s")->strftime("%Y-%m-%d %H:%M:%S"); }; sub delay { my ($dl, $cb) = @_; my $t; $t = AE::timer $dl, 0, sub { say '===> delay : delayed ', $dl, ' sec'; undef $t; $cb->() if ref $cb eq 'CODE'; }; } sub my_p_await{ my ($sec, $cond, $cb) = @_; my $d = deferred; say '===> my_p_await: staring...with ',$sec, ' sec: ', Time::Piece->new(AE::now, "%s")->strftime("%Y-%m-%d %H:%M:%S"); my $result = eval { $cond->() }; if(!$result) { say '===> my_p_await: condition is false'; delay($sec, sub { $cb->() if ref $cb eq 'CODE'; my_p_await($sec, $cond, $cb); }); }else{ say '===> my_p_await: condition is true'; $d->resolve; return; } return $d->promise; } deferred->resolve->promise->then( sub { my_p_await(3, sub { $inputs eq 'next'},sub {say '0: cb..'} ) }) ->then( sub { say ''; say '****>==> Time: 0: ', Time::Piece->new(AE::now, "%s")->strftime("%Y-%m-%d %H:%M:%S"); }); AE::cv->recv;
运行结果:
===> my_p_await: staring...with 3 sec: 2024-08-26 18:55:13 ===> my_p_await: condition is false ===> delay : delayed 3 sec 0: cb.. ===> my_p_await: staring...with 3 sec: 2024-08-26 18:55:16 ===> my_p_await: condition is false ***> AE::io timer : pass 5sec : 2024-08-26 18:55:18 ***> making : inputs = next ===> delay : delayed 3 sec 0: cb.. ===> my_p_await: staring...with 3 sec: 2024-08-26 18:55:19 ===> my_p_await: condition is true AE::io timer : passed 15 sec : 2024-08-26 18:55:28
错误原因
核心问题在于my_p_await函数的递归逻辑错误:
- 第一次调用
my_p_await时创建了一个deferred对象$d,并返回它的promise给第一个then回调。 - 当条件不满足时,递归调用
my_p_await,此时会创建新的deferred对象,但最初的$d从未被resolve。 - 第一个
then等待的是最初的promise,这个promise永远不会完成,因此后续的then回调永远不会被触发。
修复方案
修改my_p_await的逻辑,让递归检查时复用最初的deferred对象,而不是每次递归都创建新的。可以通过内部嵌套函数来保持对同一个deferred的引用:
修正后的代码:
#!/usr/bin/perl use strict; use warnings; use AnyEvent; use AE; use Time::Piece; use Promises backend =>['AnyEvent'], 'deferred'; use feature q(say); use Data::Dumper; $| = 1; my $inputs = 'starting'; my $w2; $w2 = AE::timer 5, 0, sub { say '***> AE::io timer : pass 5sec : ', Time::Piece->new(AE::now, "%s")->strftime("%Y-%m-%d %H:%M:%S"); say '***> making : inputs = next'; $inputs = 'next'; }; my $w3; $w3 = AE::timer 15, 15, sub { say 'AE::io timer : passed 15 sec : ', Time::Piece->new(AE::now, "%s")->strftime("%Y-%m-%d %H:%M:%S"); }; sub delay { my ($dl, $cb) = @_; my $t; $t = AE::timer $dl, 0, sub { say '===> delay : delayed ', $dl, ' sec'; undef $t; $cb->() if ref $cb eq 'CODE'; }; } sub my_p_await{ my ($sec, $cond, $cb) = @_; my $d = deferred; # 内部函数处理循环检查逻辑,复用同一个deferred my $check_condition = sub { say '===> my_p_await: staring...with ',$sec, ' sec: ', Time::Piece->new(AE::now, "%s")->strftime("%Y-%m-%d %H:%M:%S"); my $result = eval { $cond->() }; if(!$result) { say '===> my_p_await: condition is false'; delay($sec, sub { $cb->() if ref $cb eq 'CODE'; $check_condition->(); # 递归调用内部函数,复用同一个$d }); }else{ say '===> my_p_await: condition is true'; $d->resolve; # 条件满足时resolve最初的deferred } }; $check_condition->(); # 启动第一次检查 return $d->promise; } deferred->resolve->promise->then( sub { return my_p_await(3, sub { $inputs eq 'next'},sub {say '0: cb..'} ) # 注意要return promise }) ->then( sub { say ''; say '****>==> Time: 0: ', Time::Piece->new(AE::now, "%s")->strftime("%Y-%m-%d %H:%M:%S"); }); AE::cv->recv;
关键修复点
- 复用deferred对象:通过内部嵌套函数
$check_condition保持对同一个deferred对象$d的引用,所有检查逻辑都围绕这个对象进行,条件满足时直接resolve它。 - 返回promise到then链:第一个
then回调中必须returnmy_p_await的promise,这样then链才能正确等待它完成。
修复后运行结果
===> my_p_await: staring...with 3 sec: 2024-08-26 19:00:00 ===> my_p_await: condition is false ===> delay : delayed 3 sec 0: cb.. ===> my_p_await: staring...with 3 sec: 2024-08-26 19:00:03 ===> my_p_await: condition is false ***> AE::io timer : pass 5sec : 2024-08-26 19:00:05 ***> making : inputs = next ===> delay : delayed 3 sec 0: cb.. ===> my_p_await: staring...with 3 sec: 2024-08-26 19:00:06 ===> my_p_await: condition is true ****>==> Time: 0: 2024-08-26 19:00:06 AE::io timer : passed 15 sec : 2024-08-26 19:00:15
内容的提问来源于stack exchange,提问作者doosoonk
相关产品推荐
相关产品推荐

