假设我在一个应用程序中有许多任务,可以按任何顺序完成。我需要在所有任务完成后运行一些代码。如果这很重要,应用程序在AnyEvent下运行,但没有Coro。
在某种程度上,AnyEvent的$cv->begin/$cv->end
允许我想要的内容。但是,我希望有更细粒度的控制。例如,我想无法两次“完成”任务。从所有任务中收集数据的能力也很不错。
当然,这可以做到。设置许多共享哈希的回调;任务完成时从该哈希中删除键;当散列为空时调用megacallback。我想知道是否有更文明的方式,也许是一些CPAN模块?
例如,这是一个可以满足我需求的虚构API。
#!/usr/bin/perl -w
use strict;
use Some::Module;
# Set goals
my $cb = Some::Module->new( sub { say 'BOOM!' } );
$cb->begin( qw(foo bar) );
# Much later, as tasks start getting done
$cb->end( foo => 42 ); # "return" value from task 'foo'
$cb->begin( 'baz' ); # can add more tasks, why not
$cb->end( 'bar' ); # just finish task 'bar'
# still waiting for 'baz' to finish at this point
# Finally, last hanging task is done
$cb->end( baz => 137 ); # BOOM!
# at this point, sub {}->( { foo=>42, bar=>undef, baz=>137 } )
# has been called
另请参阅我的perlmonks question。
有这样的东西吗?
答案 0 :(得分:3)
您可能需要考虑Future。
具体来说,等待许多事情要完成,您可以使用Future->needs_all
或类似的东西:
my @things = ... # generate Futures to represent each thing
Future->needs_all( @things )
->on_done( sub {
# some code here when all the things are done
});
或者,您也可以尝试Async::MergePoint,这样可以使API更接近您的想法:
my $mp = Async::MergePoint->new( needs => [qw( foo bar )] );
$mp->close( on_done => sub {
# some code here when all the things are done
});
$mp->done( foo => $value );
$mp->done( bar => );
答案 1 :(得分:2)
我当然不是异步的专家,但我认为Mojo::IOLoop::Delay(Mojolicious套件的一部分)有一个类似的界面。请注意,Mojo :: IOLoop可以与EV and thus AnyEvent一起使用。
以下是the cookbook的示例:
use Mojo::UserAgent;
use Mojo::IOLoop;
# Synchronize non-blocking requests portably
my $ua = Mojo::UserAgent->new;
my $delay = Mojo::IOLoop->delay(sub {
my ($delay, $tx, $tx2) = @_;
...
});
$ua->get('http://mojolicio.us' => $delay->begin);
$ua->get('http://mojolicio.us/perldoc' => $delay->begin);
$delay->wait unless Mojo::IOLoop->is_running;
另请注意,$delay->begin
会返回一个基本上是end
方法的回调。
其他示例,如酷'步骤'概念,显示在::延迟文档中。
修改强>
这是一个简单的例子。请注意,在延迟类中发生了一个小的语法更改,所以这只适用于Mojolicious 3.93+,而不是以前不可能,但语法略有不同。
#!/usr/bin/env perl
use strict;
use warnings;
use v5.10;
use Mojo::IOLoop;
my $delay = Mojo::IOLoop->delay(sub{shift; say for @_});
my $end_foo = $delay->begin(0);
Mojo::IOLoop->timer( 0 => sub { $end_foo->('hi') } );
my $end_bar = $delay->begin(0);
Mojo::IOLoop->timer( 0 => sub { $end_bar->('bye') } );
$delay->wait unless $delay->ioloop->is_running; #start loop if necessary
这里我们创建延迟对象,参数是finish
事件回调。对于每个异步操作,我调用begin
,返回end
回调。默认情况下,这些回调会删除它们的第一个参数以删除多余的调用者(参见上面的示例),但我们没有这些,所以我们传递0
来表明不这样做。对于每个动作,我只是推迟零等待计时器。然后,按顺序将结束回调的参数排队等待结束事件。多田!