我尝试做同步功能
#!/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
它不会继续下一步然后,所以我从来没有看到'****>==>时间:0:'消息。
即使 AE 计时器在 15 秒后触发,AnyEvent 运行也没有问题。
第一个 5 秒计时器被触发,将 $inputs 字符串发送到 'next'。
所以 my_p_await 的条件为真。
我做错了什么?
首先,
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");
});
是一种复杂的书写方式
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");
});
在这两种情况下,
then
代码都会等待my_p_await
返回的承诺得到解决。如果条件最初为 false,则这种情况永远不会在您的代码中发生。
当然,另一个调用
my_p_await
返回的承诺会返回一个已解决的承诺。但这是一个不同的承诺。
因此,我们需要确保首次调用
my_p_wait
返回的承诺得到解决。
首先,Promises(.pm) 声称遵循 Promises 规范,但它没有提供捕获异常的 Promise 构造函数。所以我要使用固定的
deferred
。
# Promises doesn't follow the spec.
# Replace deferred with a correct one.
sub deferred {
my $cb = shift; # Optional.
my $promise = Promises::deferred;
if ( $cb ) {
eval {
$cb->(
sub { $promise->resolve( @_ ) },
sub { $promise->reject( @_ ) },
);
};
$promise->reject( $@ ) if $@;
}
return $promise;
}
接下来,我将解决“成功”回调和承诺的混合问题,这使事情变得复杂。
从技术上讲,
delay
不会混合“成功”回调和承诺。但我将把它转换为使用 Promise 来保持一致性。
sub delay {
my $sec = shift;
return deferred sub {
my $resolve = shift;
say ts(), " ===> delay: Delaying $sec s.";
my $t;
$t = AE::timer $sec, 0, sub {
say ts(), " ===> delay: Delayed $sec s.";
undef $t;
$resolve->();
};
};
}
现在让我们从
my_p_wait
中消除“成功时”回调(并重命名)。
sub polling_cond_wait {
my ( $sec, $cond ) = @_;
return deferred sub {
my $resolve = shift;
say ts(), " ===> polling_cond_wait: Start";
my $result = eval { $cond->() };
if ( $result ) {
say ts(), " ===> polling_cond_wait: Condition is true";
$resolve->();
} else {
say ts(), " ===> polling_cond_wait: Condition is false";
delay( $sec )->then( sub {
polling_cond_wait( $sec, $cond )->then( sub {
$resolve->();
} );
} );
}
};
}
只剩下主要代码了。
use strict;
use warnings;
use feature qw( say );
use AE;
use AnyEvent;
use Promises backend => [ 'AnyEvent' ];
use Time::Piece qw( localtime );
$| = 1;
sub ts { "[" . localtime->strftime( "%Y-%m-%d %H:%M:%S" ) . "]" }
# ...
my $done = AE::cv;
my $inputs = 'starting';
{
my $t; $t = AE::timer 5, 0, sub {
$t = undef;
say ts(), " ===> 5 s timer completed. Making `$inputs` next.";
$inputs = 'next';
};
}
{
my $t; $t = AE::timer 15, 0, sub {
$t = undef;
say ts(), " ===> 15 s timer completed.";
};
}
polling_cond_wait( 3, sub { $inputs eq 'next' } )->then( sub {
say ts(), " ===> then";
$done->send;
} );
$done->recv;
输出:
[2024-08-26 12:38:53] ===> polling_cond_wait: Starting.
[2024-08-26 12:38:53] ===> polling_cond_wait: Condition is false.
[2024-08-26 12:38:53] ===> delay: Delaying 3 s.
[2024-08-26 12:38:56] ===> delay: Delayed 3 s.
[2024-08-26 12:38:56] ===> polling_cond_wait: Starting.
[2024-08-26 12:38:56] ===> polling_cond_wait: Condition is false.
[2024-08-26 12:38:56] ===> delay: Delaying 3 s.
[2024-08-26 12:38:58] ===> 5 s timer completed. Making `starting` next.
[2024-08-26 12:38:59] ===> delay: Delayed 3 s.
[2024-08-26 12:38:59] ===> polling_cond_wait: Starting.
[2024-08-26 12:38:59] ===> polling_cond_wait: Condition is true.
[2024-08-26 12:38:59] ===> then
为什么要进行民意调查?
my $done = AE::cv;
my $promise = deferred;
{
my $t; $t = AE::timer 5, 0, sub {
$t = undef;
say ts(), " ===> 5 s timer completed. Resolving promise.";
$promise->resolve();
};
}
{
my $t; $t = AE::timer 15, 0, sub {
$t = undef;
say ts(), " ===> 15 s timer completed.";
};
}
$promise->then( sub {
say ts(), " ===> then";
$done->send;
} );
$done->recv;