Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also .

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
base repository: mojolicious/mojo
Choose a Base Repository
mojolicious/mojo
Acajou/mojo
Acidburn0zzz/mojo
Akron/mojo
ChinaXing/mojo
Csson/mojo
Epictetus/mojo
GeorginaGCH/mojo
GreenKakadu/mojo
GunioRobot/mojo
IronMen/mojo
Karolynn/mojo
MattOates/mojo
Mons/mojo
ProudDamsel/mojo
SPRIME01/mojo
SeHarrys/mojo
Shemahmforash/mojo
SreenathM/mojo
TristinDavis/mojo
Ufimiya/mojo
abh/mojo
aboyon/mojo
acme/mojo
adammenges/mojo
adriancdperu/mojo
ahnan4arch/mojo
ajgb/mojo
akashwebdev/mojo
alexvaluyskiy/mojo
allensh929/mojo
andrewgregory/mojo
aosiniao/mojo
areggiori/mojo
atoomic/mojo
audreyt/mojo
augensalat/mojo
avdovin/mojo
bamboodragonfly/mojo
bduggan/mojo
benvanstaveren/mojo
bfaist/mojo
bokutin/mojo
bpbhat77/mojo
brijesh1123/mojo
burbon/mojo
calumhalcrow/mojo
carloslima/mojo
cdzwm/mojo
charliebrady/mojo
charsbar/mojo
chiehwen/mojo
chromatic/mojo
cosimo/mojo
cowens/mojo
d33d/mojo
dalekx/mojo
danils/mojo
davidolrik/mojo
dekimsey/mojo
dingoe/mojo
dione/mojo
dod38fr/mojo
dolmen/p5-Mojolicious
donks122/mojo
dotandimet/mojo
drgn/mojo
dsteinbrunner/mojo
dylanklc/mojo
earonesty/mojo
edwin163com/mojo
esskar/mojo
estrai/mojo
evoyy/mojo
fritexvz/mojo
gabbs/mojo
garu/mojo
gaspachon/mojo
gbarco/mojo
gbarr/mojo
ggmonitor/mojo
ghandmann/mojo
gitlisted/mojo
glickbot/mojo
gondorz/mojo
gswirski/mojo
hanimos/mojo
hercynium/mojo
hesco/mojo
hippich/mojo
hrupprecht/mojo
hyodo/mojo
iKalin/mojo
iakuf/mojo
ian-kent/mojo
ig3/mojo
inotnako/mojo
ivanpenchev/mojo
jamadam/mojo
jamiepg1/mojo
jhannah/mojo
jleppert/mojo
jobin-sun/mojo
judofyr/mojo
k0xa/mojo
kevinold/mojo
kharayo/mojo
khkhkhMerlin/mojo
kismeter/mojo
kits/mojo
klx/mojo
kongelaks/mojo
korjavin/mojo
kvorg/mojo
kwaping/mojo
lacmkt/mojo
lammel/mojo
laudarch/mojo
lefedor/mojo
leto/mojo
lewoberst/mojo
likhatskiy/mojo
manchicken/mojo
marekkkk728/mojo
markstos/mojo
marlb/mojo
mascip/mojo
melo/mojo
memememomo/mojo
metaperl/mojo
miokebe/mojo
miyagawa/mojo
mjemmeson/mojo
mkrull/mojo
mugifly/mojo
nabu31/mojo
netao212/mojo
nicomen/mojo
nuclon/mojo
nwatkiss/mojo
omega/mojo
openfr/mojo
otoyo/mojo
ovntatar/mojo
p17n/mojo
pandureddy/mojo
pangyre/mojo
pau4o/mojo
peczenyj/mojo
perrero/mojo
physicsdude/mojo
pombredanne/mojo
ppant/mojo
pqrs7/mojo
pypy90/mojo
qqmmgg123/mojo
qwasp/mojo
raigad/mojo
raurodse/mojo
reezer/mojo
reidan/mojo
renchenyu/mojo
retf/mojo
rick-delaney/mojo
rsp/mojo
ruz/mojo
rykov/mojo
sbertrang/mojo
scao/mojo
shake014/mojo
shiba-yu36/mojo
shiftycow/mojo
shlomif/mojo
simotrone/mojo
sjn/mojo
spiros/mojo
ssoriche/mojo
stephan48/mojo
stigtsp/mojo
tempire/mojo
tfpj/mojo
tholen/mojo
tladesignz/mojo
trcjr/mojo
trinitum/mojo
user2572744/mojo
veigar/mojo
victorchalian/mojo
viliampucik/mojo
wcravens/mojo
willembasson/mojo
wudangt/mojo
xantus/mojo
xenoterracide/mojo
yakubori/mojo
yko/mojo
yuki-kimoto/mojo
zakame/mojo
zhang0137/mojo
zpsy/mojo
Nothing to show
base: v8.27
head repository: mojolicious/mojo
Choose a Head Repository
mojolicious/mojo
Acajou/mojo
Acidburn0zzz/mojo
Akron/mojo
ChinaXing/mojo
Csson/mojo
Epictetus/mojo
GeorginaGCH/mojo
GreenKakadu/mojo
GunioRobot/mojo
IronMen/mojo
Karolynn/mojo
MattOates/mojo
Mons/mojo
ProudDamsel/mojo
SPRIME01/mojo
SeHarrys/mojo
Shemahmforash/mojo
SreenathM/mojo
TristinDavis/mojo
Ufimiya/mojo
abh/mojo
aboyon/mojo
acme/mojo
adammenges/mojo
adriancdperu/mojo
ahnan4arch/mojo
ajgb/mojo
akashwebdev/mojo
alexvaluyskiy/mojo
allensh929/mojo
andrewgregory/mojo
aosiniao/mojo
areggiori/mojo
atoomic/mojo
audreyt/mojo
augensalat/mojo
avdovin/mojo
bamboodragonfly/mojo
bduggan/mojo
benvanstaveren/mojo
bfaist/mojo
bokutin/mojo
bpbhat77/mojo
brijesh1123/mojo
burbon/mojo
calumhalcrow/mojo
carloslima/mojo
cdzwm/mojo
charliebrady/mojo
charsbar/mojo
chiehwen/mojo
chromatic/mojo
cosimo/mojo
cowens/mojo
d33d/mojo
dalekx/mojo
danils/mojo
davidolrik/mojo
dekimsey/mojo
dingoe/mojo
dione/mojo
dod38fr/mojo
dolmen/p5-Mojolicious
donks122/mojo
dotandimet/mojo
drgn/mojo
dsteinbrunner/mojo
dylanklc/mojo
earonesty/mojo
edwin163com/mojo
esskar/mojo
estrai/mojo
evoyy/mojo
fritexvz/mojo
gabbs/mojo
garu/mojo
gaspachon/mojo
gbarco/mojo
gbarr/mojo
ggmonitor/mojo
ghandmann/mojo
gitlisted/mojo
glickbot/mojo
gondorz/mojo
gswirski/mojo
hanimos/mojo
hercynium/mojo
hesco/mojo
hippich/mojo
hrupprecht/mojo
hyodo/mojo
iKalin/mojo
iakuf/mojo
ian-kent/mojo
ig3/mojo
inotnako/mojo
ivanpenchev/mojo
jamadam/mojo
jamiepg1/mojo
jhannah/mojo
jleppert/mojo
jobin-sun/mojo
judofyr/mojo
k0xa/mojo
kevinold/mojo
kharayo/mojo
khkhkhMerlin/mojo
kismeter/mojo
kits/mojo
klx/mojo
kongelaks/mojo
korjavin/mojo
kvorg/mojo
kwaping/mojo
lacmkt/mojo
lammel/mojo
laudarch/mojo
lefedor/mojo
leto/mojo
lewoberst/mojo
likhatskiy/mojo
manchicken/mojo
marekkkk728/mojo
markstos/mojo
marlb/mojo
mascip/mojo
melo/mojo
memememomo/mojo
metaperl/mojo
miokebe/mojo
miyagawa/mojo
mjemmeson/mojo
mkrull/mojo
mugifly/mojo
nabu31/mojo
netao212/mojo
nicomen/mojo
nuclon/mojo
nwatkiss/mojo
omega/mojo
openfr/mojo
otoyo/mojo
ovntatar/mojo
p17n/mojo
pandureddy/mojo
pangyre/mojo
pau4o/mojo
peczenyj/mojo
perrero/mojo
physicsdude/mojo
pombredanne/mojo
ppant/mojo
pqrs7/mojo
pypy90/mojo
qqmmgg123/mojo
qwasp/mojo
raigad/mojo
raurodse/mojo
reezer/mojo
reidan/mojo
renchenyu/mojo
retf/mojo
rick-delaney/mojo
rsp/mojo
ruz/mojo
rykov/mojo
sbertrang/mojo
scao/mojo
shake014/mojo
shiba-yu36/mojo
shiftycow/mojo
shlomif/mojo
simotrone/mojo
sjn/mojo
spiros/mojo
ssoriche/mojo
stephan48/mojo
stigtsp/mojo
tempire/mojo
tfpj/mojo
tholen/mojo
tladesignz/mojo
trcjr/mojo
trinitum/mojo
user2572744/mojo
veigar/mojo
victorchalian/mojo
viliampucik/mojo
wcravens/mojo
willembasson/mojo
wudangt/mojo
xantus/mojo
xenoterracide/mojo
yakubori/mojo
yko/mojo
yuki-kimoto/mojo
zakame/mojo
zhang0137/mojo
zpsy/mojo
Nothing to show
compare: v8.28
Commits on Dec 04, 2019
Commits on Dec 24, 2019
Commits on Dec 26, 2019
Add full async/await support to Mojolicious
Showing with 465 additions and 45 deletions.
  1. +4 −0 Changes
  2. +2 −2 README.md
  3. +25 −1 lib/Mojo/Base.pm
  4. +106 −30 lib/Mojo/Promise.pm
  5. +13 −2 lib/Mojolicious.pm
  6. +9 −7 lib/Mojolicious/Command/version.pm
  7. +108 −0 lib/Mojolicious/Guides/Cookbook.pod
  8. +65 −2 t/mojo/promise.t
  9. +125 −0 t/mojo/promise_async_await.t
  10. +8 −1 t/pod_coverage.t
@@ -1,4 +1,8 @@

8.28 2019-12-26
- Added EXPERIMENTAL support for async/await (with -async Mojo::Base flag).
- Added EXPERIMENTAL all_settled and any methods to Mojo::Promise.

8.27 2019-12-04
- Added EXPERIMENTAL before_command hook.
- Added EXPERIMENTAL scope_guard function to Mojo::Util.
@@ -34,8 +34,8 @@
applications, independently of the web framework.
* Full stack HTTP and WebSocket client/server implementation with IPv6, TLS,
SNI, IDNA, HTTP/SOCKS5 proxy, UNIX domain socket, Comet (long polling),
Promises/A+, keep-alive, connection pooling, timeout, cookie, multipart,
and gzip compression support.
Promises/A+, async/await, keep-alive, connection pooling, timeout, cookie,
multipart, and gzip compression support.
* Built-in non-blocking I/O web server, supporting multiple event loops as
well as optional pre-forking and hot deployment, perfect for building
highly scalable web services.
@@ -20,6 +20,13 @@ use IO::Handle ();
use constant ROLES =>
!!(eval { require Role::Tiny; Role::Tiny->VERSION('2.000001'); 1 });

# async/await support requires Future::AsyncAwait::Frozen 0.36+
use constant ASYNC => $ENV{MOJO_NO_ASYNC} ? 0 : !!(eval {
require Future::AsyncAwait::Frozen;
Future::AsyncAwait::Frozen->VERSION('0.000001');
1;
});

# Protect subclasses using AUTOLOAD
sub DESTROY { }

@@ -120,6 +127,14 @@ sub import {
eval "package $caller; use Role::Tiny; 1" or die $@;
}

# async/await
elsif ($flag eq '-async') {
Carp::croak 'Future::AsyncAwait::Frozen 0.36+ is required for async/await'
unless ASYNC;
Future::AsyncAwait::Frozen->import_into($caller,
future_class => 'Mojo::Promise');
}

# Signatures (Perl 5.20+)
elsif ($flag eq '-signatures') {
Carp::croak 'Subroutine signatures require Perl 5.20+' if $] < 5.020;
@@ -257,14 +272,23 @@ enable support for L<subroutine signatures|perlsub/"Signatures">.
use Mojo::Base 'SomeBaseClass', -signatures;
use Mojo::Base -role, -signatures;
If you have L<Future::AsyncAwait::Frozen> 0.36+ installed you can also use the
C<-async> flag to activate the C<async> and C<await> keywords to deal much more
efficiently with promises. Note that this feature is B<EXPERIMENTAL> and might
change without warning!
# Also enable async/await
use Mojo::Base -strict, -async;
use Mojo::Base -base, -signatures, -async;
This will also disable experimental warnings on versions of Perl where this
feature was still experimental.
=head1 FLUENT INTERFACES
Fluent interfaces are a way to design object-oriented APIs around method
chaining to create domain-specific languages, with the goal of making the
readablity of the source code close to written prose.
readability of the source code close to written prose.
package Duck;
use Mojo::Base -base;
@@ -7,25 +7,35 @@ use Scalar::Util 'blessed';

has ioloop => sub { Mojo::IOLoop->singleton }, weak => 1;

sub all {
my ($class, @promises) = @_;
sub AWAIT_CLONE { shift->clone }

my $all = $promises[0]->clone;
my $results = [];
my $remaining = scalar @promises;
for my $i (0 .. $#promises) {
$promises[$i]->then(
sub {
$results->[$i] = [@_];
$all->resolve(@$results) if --$remaining <= 0;
},
sub { $all->reject(@_) }
);
}
sub AWAIT_DONE { shift->resolve(@_) }
sub AWAIT_FAIL { shift->reject(@_) }

return $all;
sub AWAIT_GET {
my $self = shift;
my @results = @{$self->{result} // []};
die $results[0] unless $self->{status} eq 'resolve';
return wantarray ? @results : $results[0];
}

sub AWAIT_IS_CANCELLED {undef}

sub AWAIT_IS_READY {
my $self = shift;
return !!$self->{result} && !@{$self->{resolve}} && !@{$self->{reject}};
}

sub AWAIT_NEW_DONE { shift->resolve(@_) }
sub AWAIT_NEW_FAIL { shift->reject(@_) }

sub AWAIT_ON_CANCEL { }
sub AWAIT_ON_READY { shift->finally(@_) }

sub all { _all(2, @_) }
sub all_settled { _all(0, @_) }
sub any { _all(3, @_) }

sub catch { shift->then(undef, shift) }

sub clone { $_[0]->new->ioloop($_[0]->ioloop) }
@@ -44,7 +54,7 @@ sub finally {

sub map {
my ($class, $options) = (shift, ref $_[0] eq 'HASH' ? shift : {});
my ($cb, @items) = @_;
my ($cb, @items) = @_;

my @start = map { $_->$cb } splice @items, 0,
$options->{concurrency} // @items;
@@ -80,12 +90,7 @@ sub new {
return $self;
}

sub race {
my ($class, @promises) = @_;
my $new = $promises[0]->clone;
$_->then(sub { $new->resolve(@_) }, sub { $new->reject(@_) }) for @promises;
return $new;
}
sub race { _all(1, @_) }

sub reject { shift->_settle('reject', @_) }
sub resolve { shift->_settle('resolve', @_) }
@@ -113,6 +118,59 @@ sub wait {
$loop->start until $done;
}

sub _all {
my ($type, $class, @promises) = @_;

my $all = $promises[0]->clone;
my $results = [];
my $remaining = scalar @promises;
for my $i (0 .. $#promises) {

# "race"
if ($type == 1) {
$promises[$i]->then(sub { $all->resolve(@_) }, sub { $all->reject(@_) });
}

# "all"
elsif ($type == 2) {
$promises[$i]->then(
sub {
$results->[$i] = [@_];
$all->resolve(@$results) if --$remaining <= 0;
},
sub { $all->reject(@_) }
);
}

# "any"
elsif ($type == 3) {
$promises[$i]->then(
sub { $all->resolve(@_) },
sub {
$results->[$i] = [@_];
$all->reject(@$results) if --$remaining <= 0;
}
);
}

# "all_settled"
else {
$promises[$i]->then(
sub {
$results->[$i] = {status => 'fulfilled', value => [@_]};
$all->resolve(@$results) if --$remaining <= 0;
},
sub {
$results->[$i] = {status => 'rejected', reason => [@_]};
$all->resolve(@$results) if --$remaining <= 0;
}
);
}
}

return $all;
}

sub _defer {
my $self = shift;

@@ -178,7 +236,7 @@ Mojo::Promise - Promises/A+
# Wrap continuation-passing style APIs with promises
my $ua = Mojo::UserAgent->new;
sub get {
sub get_p {
my $promise = Mojo::Promise->new;
$ua->get(@_ => sub {
my ($ua, $tx) = @_;
@@ -190,7 +248,7 @@ Mojo::Promise - Promises/A+
}
# Perform non-blocking operations sequentially
get('https://mojolicious.org')->then(sub {
get_p('https://mojolicious.org')->then(sub {
my $mojo = shift;
say $mojo->res->code;
return get('https://metacpan.org');
@@ -203,8 +261,8 @@ Mojo::Promise - Promises/A+
})->wait;
# Synchronize non-blocking operations (all)
my $mojo = get('https://mojolicious.org');
my $cpan = get('https://metacpan.org');
my $mojo = get_p('https://mojolicious.org');
my $cpan = get_p('https://metacpan.org');
Mojo::Promise->all($mojo, $cpan)->then(sub {
my ($mojo, $cpan) = @_;
say $mojo->[0]->res->code;
@@ -215,8 +273,8 @@ Mojo::Promise - Promises/A+
})->wait;
# Synchronize non-blocking operations (race)
my $mojo = get('https://mojolicious.org');
my $cpan = get('https://metacpan.org');
my $mojo = get_p('https://mojolicious.org');
my $cpan = get_p('https://metacpan.org');
Mojo::Promise->race($mojo, $cpan)->then(sub {
my $tx = shift;
say $tx->req->url, ' won!';
@@ -285,8 +343,26 @@ the following new ones.
Returns a new L<Mojo::Promise> object that either fulfills when all of the
passed L<Mojo::Promise> objects have fulfilled or rejects as soon as one of them
rejects. If the returned promise fulfills, it is fulfilled with the values from
the fulfilled promises in the same order as the passed promises. This method can
be useful for aggregating results of multiple promises.
the fulfilled promises in the same order as the passed promises.
=head2 all_settled
my $new = Mojo::Promise->all_settled(@promises);
Returns a new L<Mojo::Promise> object that fulfills when all of the passed
L<Mojo::Promise> objects have fulfilled or rejected, with hash references that
describe the outcome of each promise. Note that this method is B<EXPERIMENTAL>
and might change without warning!
=head2 any
my $new = Mojo::Promise->any(@promises);
Returns a new L<Mojo::Promise> object that fulfills as soon as one of
the passed L<Mojo::Promise> objects fulfills, with the value from that promise.
If no promises fulfill, it is rejected with the reasons from the rejected
promises in the same order as the passed promises. Note that this method is
B<EXPERIMENTAL> and might change without warning!
=head2 catch
@@ -59,7 +59,7 @@ has ua => sub { Mojo::UserAgent->new };
has validator => sub { Mojolicious::Validator->new };

our $CODENAME = 'Supervillain';
our $VERSION = '8.27';
our $VERSION = '8.28';

sub BUILD_DYNAMIC {
my ($class, $method, $dyn_methods) = @_;
@@ -137,7 +137,7 @@ sub handler {

# Dispatcher has to be last in the chain
++$self->{dispatch}
and $self->hook(around_action => sub { $_[2]($_[1]) })
and $self->hook(around_action => \&_action)
and $self->hook(around_dispatch => sub { $_[1]->app->dispatch($_[1]) })
unless $self->{dispatch};

@@ -197,6 +197,17 @@ sub start {

sub startup { }

sub _action {
my ($next, $c, $action, $last) = @_;

my $val = $action->($c);
$val->catch(sub { $c->helpers->reply->exception(shift) })
->finally(sub { undef $val })
if Scalar::Util::blessed $val && $val->isa('Mojo::Promise');

return $val;
}

sub _die { CORE::die ref $_[0] ? $_[0] : Mojo::Exception->new(shift)->trace }

sub _exception {
@@ -18,20 +18,22 @@ sub run {
= Mojo::IOLoop::Client->can_socks ? $IO::Socket::Socks::VERSION : 'n/a';
my $tls = Mojo::IOLoop::TLS->can_tls ? $IO::Socket::SSL::VERSION : 'n/a';
my $nnr = Mojo::IOLoop::Client->can_nnr ? $Net::DNS::Native::VERSION : 'n/a';
my $roles = Mojo::Base->ROLES ? $Role::Tiny::VERSION : 'n/a';
my $roles = Mojo::Base->ROLES ? $Role::Tiny::VERSION : 'n/a';
my $async = Mojo::Base->ASYNC ? $Future::AsyncAwait::Frozen::VERSION : 'n/a';

print <<EOF;
CORE
Perl ($^V, $^O)
Mojolicious ($Mojolicious::VERSION, $Mojolicious::CODENAME)
OPTIONAL
Cpanel::JSON::XS 4.09+ ($json)
EV 4.0+ ($ev)
IO::Socket::Socks 0.64+ ($socks)
IO::Socket::SSL 2.009+ ($tls)
Net::DNS::Native 0.15+ ($nnr)
Role::Tiny 2.000001+ ($roles)
Cpanel::JSON::XS 4.09+ ($json)
EV 4.0+ ($ev)
IO::Socket::Socks 0.64+ ($socks)
IO::Socket::SSL 2.009+ ($tls)
Net::DNS::Native 0.15+ ($nnr)
Role::Tiny 2.000001+ ($roles)
Future::AsyncAwait::Frozen 0.36+ ($async)
EOF

No commit comments for this range

You can’t perform that action at this time.