line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojo::Promise; |
2
|
63
|
|
|
63
|
|
920
|
use Mojo::Base -base; |
|
63
|
|
|
|
|
148
|
|
|
63
|
|
|
|
|
514
|
|
3
|
|
|
|
|
|
|
|
4
|
63
|
|
|
63
|
|
463
|
use Carp qw(carp croak); |
|
63
|
|
|
|
|
159
|
|
|
63
|
|
|
|
|
3356
|
|
5
|
63
|
|
|
63
|
|
20423
|
use Mojo::Exception; |
|
63
|
|
|
|
|
156
|
|
|
63
|
|
|
|
|
2680
|
|
6
|
63
|
|
|
63
|
|
978
|
use Mojo::IOLoop; |
|
63
|
|
|
|
|
136
|
|
|
63
|
|
|
|
|
420
|
|
7
|
63
|
|
|
63
|
|
337
|
use Scalar::Util qw(blessed); |
|
63
|
|
|
|
|
170
|
|
|
63
|
|
|
|
|
3622
|
|
8
|
|
|
|
|
|
|
|
9
|
63
|
|
50
|
63
|
|
386
|
use constant DEBUG => $ENV{MOJO_PROMISE_DEBUG} || 0; |
|
63
|
|
|
|
|
140
|
|
|
63
|
|
|
|
|
222751
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
has ioloop => sub { Mojo::IOLoop->singleton }, weak => 1; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
0
|
0
|
|
sub AWAIT_CHAIN_CANCEL { } |
14
|
0
|
|
|
0
|
0
|
0
|
sub AWAIT_CLONE { _await('clone', @_) } |
15
|
0
|
|
|
0
|
0
|
0
|
sub AWAIT_DONE { _settle_await(resolve => @_) } |
16
|
0
|
|
|
0
|
0
|
0
|
sub AWAIT_FAIL { _settle_await(reject => @_) } |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub AWAIT_GET { |
19
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
20
|
0
|
|
0
|
|
|
0
|
my @results = @{$self->{results} // []}; |
|
0
|
|
|
|
|
0
|
|
21
|
0
|
0
|
|
|
|
0
|
return wantarray ? @results : $results[0] if $self->{status} eq 'resolve'; |
|
|
0
|
|
|
|
|
|
22
|
0
|
0
|
0
|
|
|
0
|
die $results[0] if ref $results[0] || $results[0] =~ m!\n!; |
23
|
0
|
|
|
|
|
0
|
croak $results[0]; |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
0
|
|
|
0
|
0
|
0
|
sub AWAIT_IS_CANCELLED {undef} |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub AWAIT_IS_READY { |
29
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
30
|
0
|
|
|
|
|
0
|
$self->{handled} = 1; |
31
|
0
|
|
0
|
|
|
0
|
return !!$self->{results} && !@{$self->{resolve}} && !@{$self->{reject}}; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
0
|
|
|
0
|
0
|
0
|
sub AWAIT_NEW_DONE { _await('resolve', @_) } |
35
|
0
|
|
|
0
|
0
|
0
|
sub AWAIT_NEW_FAIL { _await('reject', @_) } |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
0
|
0
|
|
sub AWAIT_ON_CANCEL { } |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub AWAIT_ON_READY { |
40
|
0
|
|
|
0
|
0
|
0
|
shift->_finally(0, @_)->catch(sub { }); |
|
|
|
|
0
|
|
|
|
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub AWAIT_WAIT { |
44
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
45
|
0
|
|
|
0
|
|
0
|
$self->catch(sub { })->wait; |
46
|
0
|
|
|
|
|
0
|
return $self->AWAIT_GET; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub DESTROY { |
50
|
784
|
|
|
784
|
|
29058
|
my $self = shift; |
51
|
784
|
100
|
100
|
|
|
6986
|
return if $self->{handled} || ($self->{status} // '') ne 'reject' || !$self->{results}; |
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
52
|
7
|
|
|
|
|
30
|
carp "Unhandled rejected promise: @{$self->{results}}"; |
|
7
|
|
|
|
|
1052
|
|
53
|
7
|
|
|
|
|
228
|
warn $self->{debug}->message("-- Destroyed promise\n")->verbose(1)->to_string if DEBUG; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
11
|
|
|
11
|
1
|
96
|
sub all { _all(2, @_) } |
57
|
2
|
|
|
2
|
1
|
11
|
sub all_settled { _all(0, @_) } |
58
|
2
|
|
|
2
|
1
|
32
|
sub any { _all(3, @_) } |
59
|
|
|
|
|
|
|
|
60
|
177
|
|
|
177
|
1
|
1012
|
sub catch { shift->then(undef, shift) } |
61
|
|
|
|
|
|
|
|
62
|
617
|
|
|
617
|
1
|
1169
|
sub clone { $_[0]->new->ioloop($_[0]->ioloop) } |
63
|
|
|
|
|
|
|
|
64
|
11
|
|
|
11
|
1
|
77
|
sub finally { shift->_finally(1, @_) } |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub map { |
67
|
4
|
100
|
|
4
|
1
|
6363
|
my ($class, $options, $cb, @items) = (shift, ref $_[0] eq 'HASH' ? shift : {}, @_); |
68
|
|
|
|
|
|
|
|
69
|
4
|
100
|
66
|
|
|
27
|
return $class->all(map { $_->$cb } @items) if !$options->{concurrency} || @items <= $options->{concurrency}; |
|
6
|
|
|
|
|
16
|
|
70
|
|
|
|
|
|
|
|
71
|
2
|
|
|
|
|
8
|
my @start = map { $_->$cb } splice @items, 0, $options->{concurrency}; |
|
6
|
|
|
|
|
16
|
|
72
|
2
|
|
|
|
|
7
|
my @wait = map { $start[0]->clone } 0 .. $#items; |
|
6
|
|
|
|
|
11
|
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
my $start_next = sub { |
75
|
7
|
100
|
|
7
|
|
22
|
return () unless my $item = shift @items; |
76
|
4
|
|
|
|
|
9
|
my ($start_next, $chain) = (__SUB__, shift @wait); |
77
|
4
|
|
|
|
|
13
|
$_->$cb->then(sub { $chain->resolve(@_); $start_next->() }, sub { $chain->reject(@_); @items = () }) for $item; |
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
8
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
78
|
4
|
|
|
|
|
11
|
return (); |
79
|
2
|
|
|
|
|
9
|
}; |
80
|
|
|
|
|
|
|
|
81
|
2
|
|
|
3
|
|
9
|
$_->then($start_next, sub { }) for @start; |
82
|
|
|
|
|
|
|
|
83
|
2
|
|
|
|
|
9
|
return $class->all(@start, @wait); |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub new { |
87
|
790
|
|
|
790
|
1
|
77376
|
my $self = shift->SUPER::new; |
88
|
790
|
|
|
|
|
1085
|
$self->{debug} = Mojo::Exception->new->trace if DEBUG; |
89
|
790
|
100
|
|
1
|
|
1552
|
shift->(sub { $self->resolve(@_) }, sub { $self->reject(@_) }) if @_; |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
13
|
|
90
|
790
|
|
|
|
|
2055
|
return $self; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
3
|
|
|
3
|
1
|
29
|
sub race { _all(1, @_) } |
94
|
|
|
|
|
|
|
|
95
|
99
|
|
|
99
|
1
|
4866
|
sub reject { shift->_settle('reject', @_) } |
96
|
764
|
|
|
764
|
1
|
5012
|
sub resolve { shift->_settle('resolve', @_) } |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub then { |
99
|
535
|
|
|
535
|
1
|
1272
|
my ($self, $resolve, $reject) = @_; |
100
|
|
|
|
|
|
|
|
101
|
535
|
|
|
|
|
953
|
my $new = $self->clone; |
102
|
535
|
|
|
|
|
1022
|
$self->{handled} = 1; |
103
|
535
|
|
|
421
|
|
773
|
push @{$self->{resolve}}, sub { _then_cb($new, $resolve, 'resolve', @_) }; |
|
535
|
|
|
|
|
2074
|
|
|
421
|
|
|
|
|
927
|
|
104
|
535
|
|
|
88
|
|
893
|
push @{$self->{reject}}, sub { _then_cb($new, $reject, 'reject', @_) }; |
|
535
|
|
|
|
|
1670
|
|
|
88
|
|
|
|
|
200
|
|
105
|
|
|
|
|
|
|
|
106
|
535
|
100
|
|
|
|
1355
|
$self->_defer if $self->{results}; |
107
|
|
|
|
|
|
|
|
108
|
535
|
|
|
|
|
1357
|
return $new; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
3
|
|
|
3
|
1
|
3396
|
sub timer { shift->_timer('resolve', @_) } |
112
|
3
|
|
|
3
|
1
|
4336
|
sub timeout { shift->_timer('reject', @_) } |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub wait { |
115
|
46
|
|
|
46
|
1
|
128
|
my $self = shift; |
116
|
46
|
50
|
|
|
|
121
|
return if (my $loop = $self->ioloop)->is_running; |
117
|
46
|
|
|
|
|
110
|
my $done; |
118
|
46
|
|
|
31
|
|
240
|
$self->_finally(0, sub { $done++; $loop->stop })->catch(sub { }); |
|
46
|
|
|
0
|
|
99
|
|
|
46
|
|
|
|
|
193
|
|
119
|
46
|
|
|
|
|
240
|
$loop->start until $done; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub _all { |
123
|
18
|
|
|
18
|
|
58
|
my ($type, $class, @promises) = @_; |
124
|
|
|
|
|
|
|
|
125
|
18
|
|
|
|
|
61
|
my $all = $promises[0]->clone; |
126
|
18
|
|
|
|
|
58
|
my $results = []; |
127
|
18
|
|
|
|
|
43
|
my $remaining = scalar @promises; |
128
|
18
|
|
|
|
|
64
|
for my $i (0 .. $#promises) { |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# "race" |
131
|
58
|
100
|
|
|
|
164
|
if ($type == 1) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
132
|
8
|
|
|
3
|
|
36
|
$promises[$i]->then(sub { $all->resolve(@_); () }, sub { $all->reject(@_); () }); |
|
5
|
|
|
|
|
13
|
|
|
5
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
13
|
|
|
3
|
|
|
|
|
7
|
|
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# "all" |
136
|
|
|
|
|
|
|
elsif ($type == 2) { |
137
|
|
|
|
|
|
|
$promises[$i]->then( |
138
|
|
|
|
|
|
|
sub { |
139
|
32
|
|
|
32
|
|
90
|
$results->[$i] = [@_]; |
140
|
32
|
100
|
|
|
|
100
|
$all->resolve(@$results) if --$remaining <= 0; |
141
|
32
|
|
|
|
|
60
|
return (); |
142
|
|
|
|
|
|
|
}, |
143
|
4
|
|
|
4
|
|
10
|
sub { $all->reject(@_); () } |
|
4
|
|
|
|
|
5
|
|
144
|
38
|
|
|
|
|
215
|
); |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# "any" |
148
|
|
|
|
|
|
|
elsif ($type == 3) { |
149
|
|
|
|
|
|
|
$promises[$i]->then( |
150
|
2
|
|
|
2
|
|
7
|
sub { $all->resolve(@_); () }, |
|
2
|
|
|
|
|
4
|
|
151
|
|
|
|
|
|
|
sub { |
152
|
4
|
|
|
4
|
|
11
|
$results->[$i] = [@_]; |
153
|
4
|
100
|
|
|
|
24
|
$all->reject(@$results) if --$remaining <= 0; |
154
|
4
|
|
|
|
|
8
|
return (); |
155
|
|
|
|
|
|
|
} |
156
|
6
|
|
|
|
|
30
|
); |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# "all_settled" |
160
|
|
|
|
|
|
|
else { |
161
|
|
|
|
|
|
|
$promises[$i]->then( |
162
|
|
|
|
|
|
|
sub { |
163
|
5
|
|
|
5
|
|
21
|
$results->[$i] = {status => 'fulfilled', value => [@_]}; |
164
|
5
|
100
|
|
|
|
16
|
$all->resolve(@$results) if --$remaining <= 0; |
165
|
5
|
|
|
|
|
11
|
return (); |
166
|
|
|
|
|
|
|
}, |
167
|
|
|
|
|
|
|
sub { |
168
|
1
|
|
|
1
|
|
6
|
$results->[$i] = {status => 'rejected', reason => [@_]}; |
169
|
1
|
50
|
|
|
|
5
|
$all->resolve(@$results) if --$remaining <= 0; |
170
|
1
|
|
|
|
|
2
|
return (); |
171
|
|
|
|
|
|
|
} |
172
|
6
|
|
|
|
|
30
|
); |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
18
|
|
|
|
|
199
|
return $all; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _await { |
180
|
0
|
|
|
0
|
|
0
|
my ($method, $class) = (shift, shift); |
181
|
0
|
|
|
|
|
0
|
my $promise = $class->$method(@_); |
182
|
0
|
|
|
|
|
0
|
$promise->{cycle} = $promise; |
183
|
0
|
|
|
|
|
0
|
return $promise; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub _defer { |
187
|
862
|
|
|
862
|
|
1231
|
my $self = shift; |
188
|
|
|
|
|
|
|
|
189
|
862
|
50
|
|
|
|
1802
|
return unless my $results = $self->{results}; |
190
|
862
|
100
|
|
|
|
1843
|
my $cbs = $self->{status} eq 'resolve' ? $self->{resolve} : $self->{reject}; |
191
|
862
|
|
|
|
|
1578
|
@{$self}{qw(cycle resolve reject)} = (undef, [], []); |
|
862
|
|
|
|
|
2957
|
|
192
|
|
|
|
|
|
|
|
193
|
862
|
|
|
862
|
|
2397
|
$self->ioloop->next_tick(sub { $_->(@$results) for @$cbs }); |
|
862
|
|
|
|
|
2868
|
|
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub _finally { |
197
|
57
|
|
|
57
|
|
141
|
my ($self, $handled, $finally) = @_; |
198
|
|
|
|
|
|
|
|
199
|
57
|
|
|
|
|
131
|
my $new = $self->clone; |
200
|
|
|
|
|
|
|
my $cb = sub { |
201
|
57
|
|
|
57
|
|
190
|
my @results = @_; |
202
|
57
|
|
|
|
|
148
|
$new->resolve($finally->())->then(sub {@results}); |
|
55
|
|
|
|
|
145
|
|
203
|
57
|
|
|
|
|
265
|
}; |
204
|
|
|
|
|
|
|
|
205
|
57
|
|
|
|
|
122
|
my $before = $self->{handled}; |
206
|
57
|
|
|
|
|
184
|
$self->catch($cb); |
207
|
57
|
|
|
|
|
130
|
my $next = $self->then($cb); |
208
|
57
|
100
|
100
|
|
|
345
|
delete $self->{handled} if !$before && !$handled; |
209
|
|
|
|
|
|
|
|
210
|
57
|
|
|
|
|
294
|
return $next; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub _settle { |
214
|
863
|
|
|
863
|
|
1879
|
my ($self, $status, @results) = @_; |
215
|
|
|
|
|
|
|
|
216
|
863
|
|
100
|
|
|
3633
|
my $thenable = blessed $results[0] && $results[0]->can('then'); |
217
|
863
|
100
|
|
|
|
2450
|
unless (ref $self) { |
218
|
34
|
50
|
66
|
|
|
152
|
return $results[0] if $thenable && $status eq 'resolve' && $results[0]->isa('Mojo::Promise'); |
|
|
|
66
|
|
|
|
|
219
|
30
|
|
|
|
|
67
|
$self = $self->new; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
859
|
100
|
100
|
|
|
2787
|
if ($thenable && $status eq 'resolve') { |
|
|
100
|
|
|
|
|
|
223
|
67
|
|
|
54
|
|
370
|
$results[0]->then(sub { $self->resolve(@_); () }, sub { $self->reject(@_); () }); |
|
61
|
|
|
|
|
263
|
|
|
61
|
|
|
|
|
119
|
|
|
6
|
|
|
|
|
17
|
|
|
6
|
|
|
|
|
9
|
|
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
elsif (!$self->{results}) { |
226
|
761
|
|
|
|
|
1299
|
@{$self}{qw(results status)} = (\@results, $status); |
|
761
|
|
|
|
|
1863
|
|
227
|
761
|
|
|
|
|
1652
|
$self->_defer; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
859
|
|
|
|
|
4725
|
return $self; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub _settle_await { |
234
|
0
|
|
|
0
|
|
0
|
my ($status, $self, @results) = @_; |
235
|
0
|
|
|
0
|
|
0
|
return $results[0]->then(sub { $self->resolve(@_); () }, sub { $self->reject(@_); () }) |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
236
|
0
|
0
|
0
|
|
|
0
|
if blessed $results[0] && $results[0]->can('then'); |
237
|
0
|
0
|
|
|
|
0
|
@{$self}{qw(results status)} = ([@results], $status) if !$self->{results}; |
|
0
|
|
|
|
|
0
|
|
238
|
0
|
|
|
|
|
0
|
$self->_defer; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub _then_cb { |
242
|
509
|
|
|
509
|
|
1139
|
my ($new, $cb, $method, @results) = @_; |
243
|
|
|
|
|
|
|
|
244
|
509
|
100
|
|
|
|
1321
|
return $new->$method(@results) unless defined $cb; |
245
|
|
|
|
|
|
|
|
246
|
343
|
|
|
|
|
511
|
my @res; |
247
|
343
|
100
|
|
|
|
509
|
return $new->reject($@) unless eval { @res = $cb->(@results); 1 }; |
|
343
|
|
|
|
|
753
|
|
|
341
|
|
|
|
|
1077
|
|
248
|
341
|
|
|
|
|
765
|
return $new->resolve(@res); |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub _timer { |
252
|
6
|
|
|
6
|
|
32
|
my ($self, $method, $after, @results) = @_; |
253
|
6
|
100
|
|
|
|
36
|
$self = $self->new unless ref $self; |
254
|
6
|
100
|
100
|
|
|
44
|
$results[0] = 'Promise timeout' if $method eq 'reject' && !@results; |
255
|
6
|
|
|
6
|
|
28
|
$self->ioloop->timer($after => sub { $self->$method(@results) }); |
|
6
|
|
|
|
|
60
|
|
256
|
6
|
|
|
|
|
58
|
return $self; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
1; |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=encoding utf8 |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head1 NAME |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
Mojo::Promise - Promises/A+ |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=head1 SYNOPSIS |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
use Mojo::Promise; |
270
|
|
|
|
|
|
|
use Mojo::UserAgent; |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# Wrap continuation-passing style APIs with promises |
273
|
|
|
|
|
|
|
my $ua = Mojo::UserAgent->new; |
274
|
|
|
|
|
|
|
sub get_p { |
275
|
|
|
|
|
|
|
my $promise = Mojo::Promise->new; |
276
|
|
|
|
|
|
|
$ua->get(@_ => sub ($ua, $tx) { |
277
|
|
|
|
|
|
|
my $err = $tx->error; |
278
|
|
|
|
|
|
|
if (!$err || $err->{code}) { $promise->resolve($tx) } |
279
|
|
|
|
|
|
|
else { $promise->reject($err->{message}) } |
280
|
|
|
|
|
|
|
}); |
281
|
|
|
|
|
|
|
return $promise; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# Perform non-blocking operations sequentially |
285
|
|
|
|
|
|
|
get_p('https://mojolicious.org')->then(sub ($mojo) { |
286
|
|
|
|
|
|
|
say $mojo->res->code; |
287
|
|
|
|
|
|
|
return get_p('https://metacpan.org'); |
288
|
|
|
|
|
|
|
})->then(sub ($cpan) { |
289
|
|
|
|
|
|
|
say $cpan->res->code; |
290
|
|
|
|
|
|
|
})->catch(sub ($err) { |
291
|
|
|
|
|
|
|
warn "Something went wrong: $err"; |
292
|
|
|
|
|
|
|
})->wait; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# Synchronize non-blocking operations (all) |
295
|
|
|
|
|
|
|
my $mojo = get_p('https://mojolicious.org'); |
296
|
|
|
|
|
|
|
my $cpan = get_p('https://metacpan.org'); |
297
|
|
|
|
|
|
|
Mojo::Promise->all($mojo, $cpan)->then(sub ($mojo, $cpan) { |
298
|
|
|
|
|
|
|
say $mojo->[0]->res->code; |
299
|
|
|
|
|
|
|
say $cpan->[0]->res->code; |
300
|
|
|
|
|
|
|
})->catch(sub ($err) { |
301
|
|
|
|
|
|
|
warn "Something went wrong: $err"; |
302
|
|
|
|
|
|
|
})->wait; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# Synchronize non-blocking operations (race) |
305
|
|
|
|
|
|
|
my $mojo = get_p('https://mojolicious.org'); |
306
|
|
|
|
|
|
|
my $cpan = get_p('https://metacpan.org'); |
307
|
|
|
|
|
|
|
Mojo::Promise->race($mojo, $cpan)->then(sub ($tx) { |
308
|
|
|
|
|
|
|
say $tx->req->url, ' won!'; |
309
|
|
|
|
|
|
|
})->catch(sub ($err) { |
310
|
|
|
|
|
|
|
warn "Something went wrong: $err"; |
311
|
|
|
|
|
|
|
})->wait; |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=head1 DESCRIPTION |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
L is a Perl-ish implementation of L and a superset of L
|
316
|
|
|
|
|
|
|
Promises|https://duckduckgo.com/?q=\mdn%20Promise>. |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=head1 STATES |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
A promise is an object representing the eventual completion or failure of a non-blocking operation. It allows |
321
|
|
|
|
|
|
|
non-blocking functions to return values, like blocking functions. But instead of immediately returning the final value, |
322
|
|
|
|
|
|
|
the non-blocking function returns a promise to supply the value at some point in the future. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
A promise can be in one of three states: |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=over 2 |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=item pending |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
Initial state, neither fulfilled nor rejected. |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=item fulfilled |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
Meaning that the operation completed successfully. |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=item rejected |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Meaning that the operation failed. |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=back |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
A pending promise can either be fulfilled with a value or rejected with a reason. When either happens, the associated |
343
|
|
|
|
|
|
|
handlers queued up by a promise's L"then"> method are called. |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
L implements the following attributes. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=head2 ioloop |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
my $loop = $promise->ioloop; |
352
|
|
|
|
|
|
|
$promise = $promise->ioloop(Mojo::IOLoop->new); |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
Event loop object to control, defaults to the global L singleton. Note that this attribute is weakened. |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
=head1 METHODS |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
L inherits all methods from L and implements the following new ones. |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=head2 all |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
my $new = Mojo::Promise->all(@promises); |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
Returns a new L object that either fulfills when all of the passed L objects have |
365
|
|
|
|
|
|
|
fulfilled or rejects as soon as one of them rejects. If the returned promise fulfills, it is fulfilled with the values |
366
|
|
|
|
|
|
|
from the fulfilled promises in the same order as the passed promises. |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=head2 all_settled |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
my $new = Mojo::Promise->all_settled(@promises); |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
Returns a new L object that fulfills when all of the passed L objects have fulfilled or |
373
|
|
|
|
|
|
|
rejected, with hash references that describe the outcome of each promise. |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=head2 any |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
my $new = Mojo::Promise->any(@promises); |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
Returns a new L object that fulfills as soon as one of the passed L objects fulfills, |
380
|
|
|
|
|
|
|
with the value from that promise. |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=head2 catch |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
my $new = $promise->catch(sub {...}); |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
Appends a rejection handler callback to the promise, and returns a new L object resolving to the return |
387
|
|
|
|
|
|
|
value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled. |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# Longer version |
390
|
|
|
|
|
|
|
my $new = $promise->then(undef, sub {...}); |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
# Pass along the rejection reason |
393
|
|
|
|
|
|
|
$promise->catch(sub (@reason) { |
394
|
|
|
|
|
|
|
warn "Something went wrong: $reason[0]"; |
395
|
|
|
|
|
|
|
return @reason; |
396
|
|
|
|
|
|
|
}); |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
# Change the rejection reason |
399
|
|
|
|
|
|
|
$promise->catch(sub (@reason) { "This is bad: $reason[0]" }); |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=head2 clone |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
my $new = $promise->clone; |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
Return a new L object cloned from this promise that is still pending. |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=head2 finally |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
my $new = $promise->finally(sub {...}); |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
Appends a fulfillment and rejection handler to the promise, and returns a new L object resolving to the |
412
|
|
|
|
|
|
|
original fulfillment value or rejection reason. |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# Do something on fulfillment and rejection |
415
|
|
|
|
|
|
|
$promise->finally(sub { say "We are done!" }); |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
=head2 map |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
my $new = Mojo::Promise->map(sub {...}, @items); |
420
|
|
|
|
|
|
|
my $new = Mojo::Promise->map({concurrency => 3}, sub {...}, @items); |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
Apply a function that returns a L to each item in a list of items while optionally limiting concurrency. |
423
|
|
|
|
|
|
|
Returns a L that collects the results in the same manner as L. If any item's promise is rejected, |
424
|
|
|
|
|
|
|
any remaining items which have not yet been mapped will not be. |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
# Perform 3 requests at a time concurrently |
427
|
|
|
|
|
|
|
Mojo::Promise->map({concurrency => 3}, sub { $ua->get_p($_) }, @urls) |
428
|
|
|
|
|
|
|
->then(sub{ say $_->[0]->res->dom->at('title')->text for @_ }); |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
These options are currently available: |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=over 2 |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=item concurrency |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
concurrency => 3 |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
The maximum number of items that are in progress at the same time. |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=back |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=head2 new |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
my $promise = Mojo::Promise->new; |
445
|
|
|
|
|
|
|
my $promise = Mojo::Promise->new(sub {...}); |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
Construct a new L object. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# Wrap a continuation-passing style API |
450
|
|
|
|
|
|
|
my $promise = Mojo::Promise->new(sub ($resolve, $reject) { |
451
|
|
|
|
|
|
|
Mojo::IOLoop->timer(5 => sub { |
452
|
|
|
|
|
|
|
if (int rand 2) { $resolve->('Lucky!') } |
453
|
|
|
|
|
|
|
else { $reject->('Unlucky!') } |
454
|
|
|
|
|
|
|
}); |
455
|
|
|
|
|
|
|
}); |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=head2 race |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
my $new = Mojo::Promise->race(@promises); |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
Returns a new L object that fulfills or rejects as soon as one of the passed L objects |
462
|
|
|
|
|
|
|
fulfills or rejects, with the value or reason from that promise. |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=head2 reject |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
my $new = Mojo::Promise->reject(@reason); |
467
|
|
|
|
|
|
|
$promise = $promise->reject(@reason); |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Build rejected L object or reject the promise with one or more rejection reasons. |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
# Longer version |
472
|
|
|
|
|
|
|
my $promise = Mojo::Promise->new->reject(@reason); |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=head2 resolve |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
my $new = Mojo::Promise->resolve(@value); |
477
|
|
|
|
|
|
|
$promise = $promise->resolve(@value); |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Build resolved L object or resolve the promise with one or more fulfillment values. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# Longer version |
482
|
|
|
|
|
|
|
my $promise = Mojo::Promise->new->resolve(@value); |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=head2 then |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
my $new = $promise->then(sub {...}); |
487
|
|
|
|
|
|
|
my $new = $promise->then(sub {...}, sub {...}); |
488
|
|
|
|
|
|
|
my $new = $promise->then(undef, sub {...}); |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
Appends fulfillment and rejection handlers to the promise, and returns a new L object resolving to the |
491
|
|
|
|
|
|
|
return value of the called handler. |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# Pass along the fulfillment value or rejection reason |
494
|
|
|
|
|
|
|
$promise->then( |
495
|
|
|
|
|
|
|
sub (@value) { |
496
|
|
|
|
|
|
|
say "The result is $value[0]"; |
497
|
|
|
|
|
|
|
return @value; |
498
|
|
|
|
|
|
|
}, |
499
|
|
|
|
|
|
|
sub (@reason) { |
500
|
|
|
|
|
|
|
warn "Something went wrong: $reason[0]"; |
501
|
|
|
|
|
|
|
return @reason; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
); |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
# Change the fulfillment value or rejection reason |
506
|
|
|
|
|
|
|
$promise->then( |
507
|
|
|
|
|
|
|
sub (@value) { return "This is good: $value[0]" }, |
508
|
|
|
|
|
|
|
sub (@reason) { return "This is bad: $reason[0]" } |
509
|
|
|
|
|
|
|
); |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=head2 timer |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
my $new = Mojo::Promise->timer(5 => 'Success!'); |
514
|
|
|
|
|
|
|
$promise = $promise->timer(5 => 'Success!'); |
515
|
|
|
|
|
|
|
$promise = $promise->timer(5); |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Create a new L object with a timer or attach a timer to an existing promise. The promise will be |
518
|
|
|
|
|
|
|
resolved after the given amount of time in seconds with or without a value. |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=head2 timeout |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
my $new = Mojo::Promise->timeout(5 => 'Timeout!'); |
523
|
|
|
|
|
|
|
$promise = $promise->timeout(5 => 'Timeout!'); |
524
|
|
|
|
|
|
|
$promise = $promise->timeout(5); |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
Create a new L object with a timeout or attach a timeout to an existing promise. The promise will be |
527
|
|
|
|
|
|
|
rejected after the given amount of time in seconds with a reason, which defaults to C. |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=head2 wait |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
$promise->wait; |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
Start L"ioloop"> and stop it again once the promise has been fulfilled or rejected, does nothing when L"ioloop"> is |
534
|
|
|
|
|
|
|
already running. |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=head1 DEBUGGING |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
You can set the C environment variable to get some advanced diagnostics information printed to |
539
|
|
|
|
|
|
|
C. |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
MOJO_PROMISE_DEBUG=1 |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=head1 SEE ALSO |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
L, L, L. |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=cut |