line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Data::Promise; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Data::Promise - simple promise like interface |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use Modern::Perl; |
10
|
|
|
|
|
|
|
use Data::Promose; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $p=new Data::Promise(cb=>sub { |
13
|
|
|
|
|
|
|
my ($resolve,$reject)=@_; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
if(...) { |
16
|
|
|
|
|
|
|
# pass context |
17
|
|
|
|
|
|
|
$resolve->('ok'); |
18
|
|
|
|
|
|
|
} else { |
19
|
|
|
|
|
|
|
$reject->('something went wrong'); |
20
|
|
|
|
|
|
|
} |
21
|
|
|
|
|
|
|
}); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub pass_function { ... } |
24
|
|
|
|
|
|
|
sub fail_function { ... } |
25
|
|
|
|
|
|
|
$p->then(\&pass_function,\&fail_function); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# delayed example |
29
|
|
|
|
|
|
|
my $p=new Data::Promise( |
30
|
|
|
|
|
|
|
delayed=>1, |
31
|
|
|
|
|
|
|
cb=>sub { |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
if(...) { |
34
|
|
|
|
|
|
|
# pass context |
35
|
|
|
|
|
|
|
$resolve->('ok'); |
36
|
|
|
|
|
|
|
} else { |
37
|
|
|
|
|
|
|
$reject->('something went wrong'); |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
}); |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
$p->then(\&pass_function,\&fail_function); |
42
|
|
|
|
|
|
|
# pass and fail functions will not be called until now |
43
|
|
|
|
|
|
|
$p->do_resolve; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
## create a rejected promise |
46
|
|
|
|
|
|
|
my $p=Data::Promise->reject(42); |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# you can be sure your fail funtion will be called |
49
|
|
|
|
|
|
|
$p->then(\&pass_function,\&fail_function); |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
## create a resolved promise |
52
|
|
|
|
|
|
|
my $p=Data::Promise->resolve(42); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# you can be sure your pass funtion will be called |
55
|
|
|
|
|
|
|
$p->then(\&pass_function,\&fail_function); |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head1 DESCRIPTION |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
A light and simple Promise object based on the current es6 implementation. This promise object class was written to mimic how promise(s) are implemnted in the wild. This may or may not be the class you are looking for. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=cut |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
our $VERSION=0.001; |
64
|
|
|
|
|
|
|
|
65
|
1
|
|
|
1
|
|
780
|
use Modern::Perl; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
7
|
|
66
|
1
|
|
|
1
|
|
742
|
use Moo; |
|
1
|
|
|
|
|
11072
|
|
|
1
|
|
|
|
|
4
|
|
67
|
1
|
|
|
1
|
|
1981
|
use MooX::Types::MooseLike::Base qw(:all); |
|
1
|
|
|
|
|
6542
|
|
|
1
|
|
|
|
|
401
|
|
68
|
|
|
|
|
|
|
|
69
|
1
|
|
|
1
|
|
512
|
use namespace::clean; |
|
1
|
|
|
|
|
11341
|
|
|
1
|
|
|
|
|
9
|
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head1 OO Constructor Arguments |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=over 4 |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item * cb=>sub { my ($resovle,$reject)=@_ } |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
The callback function used to resolve the object. If no function is passed in then the object will never resolve! |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=cut |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
has cb=>( |
82
|
|
|
|
|
|
|
isa=>CodeRef, |
83
|
|
|
|
|
|
|
default=>\&_build_stub, |
84
|
|
|
|
|
|
|
is=>'ro', |
85
|
|
|
|
|
|
|
); |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=item * delayed=>0|1 |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
This enables or disables manual control over when your cb function will be called. The default is false. |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=cut |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
has delayed=>( |
94
|
|
|
|
|
|
|
isa=>Bool, |
95
|
|
|
|
|
|
|
is=>'ro', |
96
|
|
|
|
|
|
|
default=>0, |
97
|
|
|
|
|
|
|
); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=back |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=cut |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
has _jobs=>( |
104
|
|
|
|
|
|
|
isa=>ArrayRef, |
105
|
|
|
|
|
|
|
is=>'ro', |
106
|
|
|
|
|
|
|
default=>sub {[]}, |
107
|
|
|
|
|
|
|
); |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
has _finally=>( |
110
|
|
|
|
|
|
|
isa=>ArrayRef, |
111
|
|
|
|
|
|
|
is=>'ro', |
112
|
|
|
|
|
|
|
default=>sub {[]}, |
113
|
|
|
|
|
|
|
); |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
has _then_catch=>( |
116
|
|
|
|
|
|
|
isa=>ArrayRef, |
117
|
|
|
|
|
|
|
is=>'ro', |
118
|
|
|
|
|
|
|
default=>sub {[]}, |
119
|
|
|
|
|
|
|
); |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
has _pending=>( |
122
|
|
|
|
|
|
|
is=>'rw', |
123
|
|
|
|
|
|
|
); |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
has _result=>( |
126
|
|
|
|
|
|
|
is=>'rw', |
127
|
|
|
|
|
|
|
); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub _build_stub { |
130
|
0
|
|
|
0
|
|
0
|
return \&_default_stub |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
0
|
|
|
sub _default_stub { } |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=head1 Promise functions |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=head2 if($p->pending) { ... } |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Used as a state checking interface, this method returns true if the promise is still being resolved, false if it is not. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=cut |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub pending { |
144
|
30
|
|
|
30
|
0
|
1505
|
my ($self)=@_; |
145
|
30
|
100
|
|
|
|
134
|
return defined($self->_pending) ? 0 : 1; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=head2 my $p=$p->then(\&resolve,\&reject) |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
This method provides a way to attach functions that will be called when the object is either rejected or resovled. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=cut |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub then { |
155
|
6
|
|
|
6
|
1
|
14
|
my ($self,$resolve,$reject)=@_; |
156
|
|
|
|
|
|
|
|
157
|
6
|
100
|
|
0
|
|
21
|
$resolve=sub {} unless defined($resolve); |
158
|
6
|
100
|
|
0
|
|
16
|
$reject=sub {} unless defined($reject); |
159
|
6
|
100
|
|
|
|
14
|
if($self->pending) { |
160
|
2
|
|
|
|
|
5
|
push @{$self->_jobs},[$resolve,$reject]; |
|
2
|
|
|
|
|
8
|
|
161
|
|
|
|
|
|
|
} else { |
162
|
4
|
100
|
|
|
|
14
|
my $code=$self->_pending==0 ? $resolve : $reject; |
163
|
4
|
|
|
|
|
5
|
eval { $code->(@{$self->_result}) }; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
13
|
|
164
|
|
|
|
|
|
|
} |
165
|
6
|
|
|
|
|
27
|
return $self; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=head2 my $p=$p->catch(\&reject) |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
This is really a wrapper function for: $p->then(undef,\&reject); |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=cut |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub catch { |
175
|
0
|
|
|
0
|
1
|
0
|
my ($self,$code)=@_; |
176
|
0
|
|
|
|
|
0
|
$self->then(undef,$code); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head2 my $p=Data::Promise->reject(@args) |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
Creates a rejected promise with @args as the rejected data. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=cut |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub reject { |
186
|
1
|
|
|
1
|
1
|
28
|
my ($class,@args)=@_; |
187
|
|
|
|
|
|
|
return __PACKAGE__->new(cb=>sub { |
188
|
1
|
|
|
1
|
|
4
|
my ($pass,$fail)=@_; |
189
|
1
|
|
|
|
|
3
|
$fail->(@args); |
190
|
1
|
|
|
|
|
23
|
}); |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=head2 my $p=Data::Promise->resolve(@args) |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
Creates a resolved promise with @args as the resolved data. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=cut |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub resolve { |
200
|
1
|
|
|
1
|
1
|
28
|
my ($class,@args)=@_; |
201
|
|
|
|
|
|
|
return __PACKAGE__->new(cb=>sub { |
202
|
1
|
|
|
1
|
|
3
|
my ($pass,$fail)=@_; |
203
|
1
|
|
|
|
|
3
|
$pass->(@args); |
204
|
1
|
|
|
|
|
22
|
}); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=head2 my $p=$p->finally(sub {}); |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Allows the addition of functions that will be called once the object is resolved. The functions will recive no arguments, and are called reguardless of the resolved or rejected state. |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=cut |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub finally { |
214
|
4
|
|
|
4
|
1
|
8
|
my ($self,$code)=@_; |
215
|
|
|
|
|
|
|
|
216
|
4
|
50
|
|
0
|
|
10
|
$code=sub {} unless defined($code); |
217
|
|
|
|
|
|
|
|
218
|
4
|
100
|
|
|
|
8
|
if($self->pending) { |
219
|
2
|
|
|
|
|
6
|
push @{$self->_finally},$code; |
|
2
|
|
|
|
|
7
|
|
220
|
|
|
|
|
|
|
} else { |
221
|
2
|
|
|
|
|
5
|
eval { $code->() } |
|
2
|
|
|
|
|
12
|
|
222
|
|
|
|
|
|
|
} |
223
|
4
|
|
|
|
|
24
|
return $self; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub _resolver { |
227
|
10
|
|
|
10
|
|
20
|
my ($self,$col)=@_; |
228
|
|
|
|
|
|
|
return sub { |
229
|
|
|
|
|
|
|
|
230
|
6
|
100
|
|
6
|
|
17
|
return unless $self->pending; |
231
|
4
|
|
|
|
|
9
|
my $args=[@_]; |
232
|
4
|
|
|
|
|
13
|
$self->_result($args); |
233
|
4
|
|
|
|
|
9
|
$self->_pending($col); |
234
|
4
|
|
|
|
|
8
|
foreach my $funcs (@{$self->_jobs}) { |
|
4
|
|
|
|
|
12
|
|
235
|
2
|
|
|
|
|
4
|
eval { |
236
|
2
|
|
|
|
|
3
|
$funcs->[$col]->(@{$args}); |
|
2
|
|
|
|
|
6
|
|
237
|
|
|
|
|
|
|
}; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
} |
240
|
10
|
|
|
|
|
46
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
sub BUILD { |
243
|
4
|
|
|
4
|
0
|
391
|
my ($self)=@_; |
244
|
|
|
|
|
|
|
|
245
|
4
|
100
|
|
|
|
24
|
return if $self->delayed; |
246
|
2
|
|
|
|
|
11
|
$self->do_resolve; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=head2 my $p=$p->do_resolve |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
When the promise is constructed in a delayed state, this method must be called to activate the cb method. |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=cut |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub do_resolve { |
256
|
6
|
|
|
6
|
1
|
16
|
my ($self)=@_; |
257
|
|
|
|
|
|
|
|
258
|
6
|
100
|
|
|
|
13
|
return unless $self->pending; |
259
|
4
|
|
|
|
|
12
|
my ($pass,$fail)=($self->_resolver(0),$self->_resolver(1)); |
260
|
4
|
|
|
|
|
6
|
eval { |
261
|
4
|
|
|
|
|
14
|
$self->cb->( |
262
|
|
|
|
|
|
|
$pass, |
263
|
|
|
|
|
|
|
$fail, |
264
|
|
|
|
|
|
|
); |
265
|
|
|
|
|
|
|
}; |
266
|
4
|
50
|
|
|
|
19
|
if($@) { |
267
|
0
|
|
|
|
|
0
|
$fail->($@); |
268
|
|
|
|
|
|
|
} |
269
|
4
|
|
|
|
|
5
|
foreach my $f (@{$self->_finally}) { |
|
4
|
|
|
|
|
10
|
|
270
|
2
|
|
|
|
|
3
|
eval { |
271
|
2
|
|
|
|
|
6
|
$f->(); |
272
|
|
|
|
|
|
|
}; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# clean up all code refs |
276
|
4
|
|
|
|
|
8
|
@{$self->_jobs}=(); |
|
4
|
|
|
|
|
16
|
|
277
|
4
|
|
|
|
|
6
|
@{$self->_finally}=(); |
|
4
|
|
|
|
|
10
|
|
278
|
4
|
|
|
|
|
30
|
return $self; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub DEMOLISH { |
282
|
4
|
|
|
4
|
0
|
2870
|
my ($self)=@_; |
283
|
4
|
50
|
|
|
|
13
|
return unless defined($self); |
284
|
4
|
|
|
|
|
8
|
%{$self}=(); |
|
4
|
|
|
|
|
30
|
|
285
|
4
|
|
|
|
|
68
|
undef $self; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=head1 AUTHOR |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
Michael Shipper |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
=cut |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
1; |