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 }