AnyEvent::Capture - Synchronous calls of async APIS
I’ve been a busy little bee lately, and have published a handful of new CPAN modules— I’ll be posting about all of them, but to start things off, I bring you: AnyEvent::Capture
It adds a little command to make calling async APIs in a synchronous, but non-blocking manner easy. Let’s start with an example of how you might do this without my shiny new module:
use AnyEvent::Socket qw( inet_aton );
my $cv = AE::cv;
inet_aton( 'localhost', sub { $cv->send(@_) });
my @ips = $cv->recv;
say join ".", unpack("C*") for @ips;
The above is not an uncommon pattern when using AnyEvent, especially in libraries, where your code should block, but you don’t want to block other event listeners. AnyEvent::Capture makes this pattern a lot cleaner:
use AnyEvent::Capture;
use AnyEvent::Socket qw( inet_aton );
my @ips = capture { inet_aton( 'localhost', shift ) };
say join ".", unpack("C*") for @ips;
The AnyEvent::DBus documentation provides another excellent example of just how awkward this can be:
use AnyEvent;
use AnyEvent::DBus;
use Net::DBus::Annotation qw(:call);
my $conn = Net::DBus->find; # always blocks :/
my $bus = $conn->get_bus_object;
my $quit = AE::cv;
$bus->ListNames (dbus_call_async)->set_notify (sub {
for my $name (@{ $_[0]->get_result }) {
print " $name\n";
}
$quit->send;
});
$quit->recv;
With AnyEvent::Capture this would be:
use AnyEvent;
use AnyEvent::Capture;
use AnyEvent::DBus;
use Net::DBus::Annotation qw(:call);
my $conn = Net::DBus->find; # always blocks :/
my $bus = $conn->get_bus_object;
my $reply = capture { $bus->ListNames(dbus_call_async)->set_notify(shift) };
for my $name (@{ $reply->get_result }) {
print " $name\n";
}
We can also find similar examples in the Coro documentation, where rouse_cb/rouse_wait replace condvars:
sub wait_for_child($) {
my ($pid) = @_;
my $watcher = AnyEvent->child (pid => $pid, cb => Coro::rouse_cb);
my ($rpid, $rstatus) = Coro::rouse_wait;
$rstatus
}
Even still, for the common case, AnyEvent::Capture provides a much cleaner interface, especially as it will manage the guard object for you.
sub wait_for_child($) {
my ($pid) = @_;
my($rpid, $rstatus) = capture { AnyEvent->child (pid => $pid, cb => shift) };
$rstatus
}