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