package Future::Mojo;
use strict;
use warnings;
use Carp 'croak';
use Scalar::Util 'blessed', 'weaken';
use Mojo::IOLoop;
use Role::Tiny::With;
use parent 'Future';
our $VERSION = '1.002';
with 'Future::Role::Promisify';
sub new {
my $proto = shift;
my $self = $proto->SUPER::new;
$self->{loop} = ref $proto ? $proto->{loop} : (shift() // Mojo::IOLoop->singleton);
return $self;
}
sub new_timer {
my $proto = shift;
my $self = (blessed $_[0] and $_[0]->isa('Mojo::IOLoop'))
? $proto->new(shift) : $proto->new;
$self->_set_timer(1, @_);
return $self;
}
sub new_timeout {
my $proto = shift;
my $self = (blessed $_[0] and $_[0]->isa('Mojo::IOLoop'))
? $proto->new(shift) : $proto->new;
$self->_set_timer(0, @_);
return $self;
}
sub _set_timer {
my ($self, $succeed, $after) = @_;
weaken(my $weakself = $self);
my $cb = $succeed ? sub { $weakself->done if $weakself }
: sub { $weakself->fail('Timeout') if $weakself };
my $id = $self->loop->timer($after => $cb);
$self->on_cancel(sub { shift->loop->remove($id) });
return $self;
}
sub loop { shift->{loop} }
sub await {
my $self = shift;
croak 'Awaiting a future while the event loop is running would recurse'
if $self->{loop}->is_running;
$self->{loop}->one_tick until $self->is_ready;
}
sub done_next_tick {
weaken(my $self = shift);
my @result = @_;
$self->loop->next_tick(sub { $self->done(@result) if $self });
return $self;
}
sub fail_next_tick {
weaken(my $self = shift);
my ($exception, @details) = @_;
croak 'Expected a true exception' unless $exception;
$self->loop->next_tick(sub { $self->fail($exception, @details) if $self });
return $self;
}
1;
=head1 NAME
Future::Mojo - use Future with Mojo::IOLoop
=head1 SYNOPSIS
use Future::Mojo;
use Mojo::IOLoop;
my $loop = Mojo::IOLoop->new;
my $future = Future::Mojo->new($loop);
$loop->timer(3 => sub { $future->done('Done') });
print $future->get, "\n";
=head1 DESCRIPTION
This subclass of L<Future> stores a reference to the associated L<Mojo::IOLoop>
instance, allowing the C<await> method to block until the Future is ready.
For a full description on how to use Futures, see the L<Future> documentation.
=head1 CONSTRUCTORS
=head2 new
my $future = Future::Mojo->new;
my $future = Future::Mojo->new($loop);
Returns a new Future. Uses L<Mojo::IOLoop/"singleton"> if no loop is specified.
=head2 new_timer
my $future = Future::Mojo->new_timer($seconds);
my $future = Future::Mojo->new_timer($loop, $seconds);
Returns a new Future that will become ready after the specified delay. Uses
L<Mojo::IOLoop/"singleton"> if no loop is specified.
=head2 new_timeout
my $future = Future::Mojo->new_timeout($seconds);
my $future = Future::Mojo->new_timeout($loop, $seconds);
Returns a new Future that will fail after the specified delay. Uses
L<Mojo::IOLoop/"singleton"> if no loop is specified.
=head1 METHODS
L<Future::Mojo> inherits all methods from L<Future> and implements the
following new ones.
=head2 loop
$loop = $future->loop;
Returns the underlying L<Mojo::IOLoop> object.
=head2 await
$future->await;
Runs the underlying L<Mojo::IOLoop> until the future is ready. If the event
loop is already running, an exception is thrown.
=head2 done_next_tick
$future = $future->done_next_tick(@result);
A shortcut to calling the L<Future/"done"> method on the
L<Mojo::IOLoop/"next_tick">. Ensures that a returned Future object is not ready
immediately, but will wait for the next I/O round.
=head2 fail_next_tick
$future = $future->fail_next_tick($exception, @details);
A shortcut to calling the L<Future/"fail"> method on the
L<Mojo::IOLoop/"next_tick">. Ensures that a returned Future object is not ready
immediately, but will wait for the next I/O round.
=head2 promisify
my $promise = $future->promisify;
Composed from L<Future::Role::Promisify>.
=head1 BUGS
Report any issues on the public bugtracker.
=head1 AUTHOR
Dan Book <dbook@cpan.org>
=head1 CONTRIBUTORS
=over
=item Jose Luis Martinez (pplu)
=back
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2015 by Dan Book.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
=head1 SEE ALSO
L<Future>