File Coverage

blib/lib/Evo/Promise/Role.pm
Criterion Covered Total %
statement 202 205 98.5
branch 76 116 65.5
condition 2 3 66.6
subroutine 30 30 100.0
pod 0 17 0.0
total 310 371 83.5


line stmt bran cond sub pod time code
1             package Evo::Promise::Role;
2 11     11   6659 use Evo -Class;
  11         30  
  11         100  
3 11     11   83 use Evo '-Promise::Sync; -Lib try; -Promise::Const *; -Promise::Deferred';
  11         29  
  11         43  
4 11     11   87 use Evo 'Carp croak; Scalar::Util blessed';
  11         25  
  11         49  
5              
6             requires 'postpone';
7              
8             # https://promisesaplus.com/
9              
10             has $_, optional for qw(d_v d_locked d_fh d_rh d_settled);
11             has 'd_children' => ro, sub { [] };
12             has 'state' => PENDING;
13              
14             #sub assert { shift or croak join '; ', caller() }
15              
16             #sub value($self) {
17             # croak "$self isn't fulfilled" unless $self->state eq FULFILLED;
18             # $self->d_v;
19             #}
20             #
21             #sub reason($self) {
22             # croak "$self isn't rejected" unless $self->state eq REJECTED;
23             # $self->d_v;
24             #}
25              
26             ## CLASS METHODS
27 2 50   2 0 1314 sub promise ($me, $fn) {
  2 50       10  
  2         7  
  2         5  
  2         5  
28 2         30 my $d = Evo::Promise::Deferred->new(promise => my $p = $me->new());
29             try {
30 2     2   17 $fn->(sub { $d->resolve(@_) }, sub { $d->reject(@_) });
  1         17  
  0         0  
31             }
32 1 50   1   20 sub($e) {
  1 50       7  
  1         4  
  1         4  
33 1         8 $d->reject(@_);
34 2         32 };
35 2         47 $p;
36             }
37              
38 1 50   1 0 299 sub deferred($me) { Evo::Promise::Deferred->new(promise => $me->new()); }
  1 50       7  
  1         3  
  1         2  
  1         11  
39              
40 10 50   10 0 3011 sub resolve ($me, $v) {
  10 50       39  
  10         21  
  10         20  
  10         16  
41 10         73 my $d = Evo::Promise::Deferred->new(promise => $me->new());
42 10         42 $d->resolve($v);
43 10         68 $d->promise;
44             }
45              
46 10 50   10 0 1625 sub reject ($me, $v) {
  10 50       29  
  10         20  
  10         17  
  10         15  
47 10         59 my $d = Evo::Promise::Deferred->new(promise => $me->new());
48 10         41 $d->reject($v);
49 10         51 $d->promise;
50             }
51              
52 7 50   7 0 90 sub race ($me, @prms) {
  7         18  
  7         34  
  7         17  
53 7         49 my $d = Evo::Promise::Deferred->new(promise => $me->new());
54 7     10   38 my $onF = sub { $d->resolve(@_) };
  10         45  
55 7     6   26 my $onR = sub { $d->reject(@_) };
  6         30  
56 7         22 foreach my $cur (@prms) {
57 17 50       63 if (ref $cur eq 'Evo::Promise::Class') {
58 0         0 $cur->then($onF, $onR);
59             }
60             else {
61             # wrap with our promise
62 17         121 my $wd = Evo::Promise::Deferred->new(promise => $me->new());
63 17         93 $wd->promise->then($onF, $onR);
64 17         69 $wd->resolve($cur);
65             }
66             }
67              
68 7         55 $d->promise;
69             }
70              
71              
72 6 50   6 0 69 sub all ($me, @prms) {
  6         12  
  6         19  
  6         11  
73 6         33 my $d = Evo::Promise::Deferred->new(promise => $me->new());
74 6 100       15 do { $d->resolve([]); return $d->promise; } unless @prms;
  1         6  
  1         16  
75              
76 5         9 my $pending = @prms;
77              
78 5         6 my @result;
79 5     6   17 my $onR = sub { $d->reject($_[0]) };
  6         16  
80              
81 5         18 for (my $i = 0; $i < @prms; $i++) {
82 20         35 my $cur_i = $i;
83 20         33 my $cur_p = $prms[$cur_i];
84 20 100   14   60 my $onF = sub { $result[$cur_i] = $_[0]; $d->resolve(\@result) if --$pending == 0; };
  14         30  
  14         47  
85              
86 20 50       51 if (ref $cur_p eq 'Evo::Promise::Class') {
87 0         0 $cur_p->then($onF, $onR);
88             }
89             else {
90             # wrap with our promise
91 20         99 my $wd = Evo::Promise::Deferred->new(promise => $me->new());
92 20         104 $wd->promise->then($onF, $onR);
93 20         54 $wd->resolve($cur_p);
94             }
95             }
96 5         47 $d->promise;
97             }
98              
99             ### OBJECT METHODS
100              
101 9 50   9 0 100 sub finally ($self, $fn) {
  9 50       21  
  9         12  
  9         13  
  9         12  
102 9         41 my $d = Evo::Promise::Deferred->new(promise => ref($self)->new);
103 9         19 my $me = ref($self);
104 5 50   5   12 my $onF = sub($v) {
  5 50       14  
  5         9  
  5         6  
105 5         12 $d->resolve($fn->()); # need pass result because it can be a promise
106 4         27 $d->promise->then(sub {$v});
  3         5  
107 9         42 };
108 4 50   4   13 my $onR = sub($r) {
  4 50       11  
  4         8  
  4         7  
109 4         10 $d->resolve($fn->()); # see above
110 3         15 $d->promise->then(sub { $me->reject($r) });
  2         7  
111 9         27 };
112 9         24 $self->then($onF, $onR);
113             }
114              
115 9 50   9 0 39 sub catch ($self, $cfn) {
  9 50       29  
  9         18  
  9         17  
  9         17  
116 9         28 $self->then(undef, $cfn);
117             }
118              
119 2 50   2 0 25 sub spread ($self, $fn) {
  2 50       11  
  2         7  
  2         4  
  2         5  
120 2 50   2   21 $self->then(sub($ref) { $fn->($ref->@*) });
  2 50       14  
  2         9  
  2         5  
  2         5  
  2         7  
121             }
122              
123              
124             sub then {
125 87     87 0 1929 my ($self, $fh, $rh) = @_;
126 87 100       695 my $p = ref($self)->new(ref($fh) ? (d_fh => $fh) : (), ref($rh) ? (d_rh => $rh) : ());
    100          
127 87         274 push $self->d_children->@*, $p;
128 87 100       304 $self->d_traverse if $self->d_settled;
129 87         308 $p;
130             }
131              
132             ### DRIVER INTERNAL METHODS
133              
134 37 50   37 0 113 sub d_lock_in ($self, $parent) {
  37 50       107  
  37         73  
  37         73  
  37         69  
135              
136             #assert(!$self->d_locked);
137             #assert(!$self->d_settled);
138 37         172 unshift $parent->d_children->@*, $self->d_locked(1);
139             }
140              
141 160 50   160 0 430 sub d_fulfill ($self, $v) {
  160 50       391  
  160         261  
  160         274  
  160         285  
142              
143             #assert(!$self->d_settled);
144 160         846 $self->d_settled(1)->state(FULFILLED)->d_v($v);
145             }
146              
147 67 50   67 0 199 sub d_reject ($self, $r) {
  67 50       171  
  67         140  
  67         129  
  67         116  
148              
149             #assert(!$_[0]->d_settled);
150 67         345 $self->d_settled(1)->state(REJECTED)->d_v($r);
151             }
152              
153             # 2.3 The Promise Resolution Procedure
154             # 2.3.3.2, 2.3.3.4 doesn't make sense in perl (in real world)
155             # Changed term obj or func to blessed obj and can "then"
156 165 50   165 0 455 sub d_resolve ($self, $x) {
  165 50       394  
  165         300  
  165         277  
  165         253  
157              
158             #assert(!$self->d_settled);
159              
160 165         271 while (1) {
161              
162             # 2.3.4 but means not a blessed object
163 169 100       708 return $self->d_fulfill($x) unless blessed($x);
164              
165              
166             # 2.3.1
167 58 100 66     411 return $self->d_reject('TypeError') if $x && $self eq $x;
168              
169             # 2.3.2 promise
170 57 100       184 if (ref $x eq ref $self) {
171 46 100       243 $x->d_settled
    100          
172             ? $x->state eq FULFILLED
173             ? $self->d_fulfill($x->d_v)
174             : $self->d_reject($x->d_v)
175             : $self->d_lock_in($x);
176 46         133 return;
177             }
178              
179 11 100       72 if ($x->can('then')) {
180 10         121 my $sync = Evo::Promise::Sync->new(promise => $self)->try_thenable($x);
181 10 100       50 return unless $sync->should_resolve;
182 4         11 $x = $sync->v; # and next, but it's already last in loop
183 4         20 next;
184             }
185              
186             # 2.3.3.4
187 1         5 return $self->d_fulfill($x);
188             }
189             }
190              
191             # reject promise and call traverse with the stack of children
192 32 50   32 0 166 sub d_reject_continue ($self, $reason) {
  32 50       98  
  32         66  
  32         67  
  32         58  
193 32         126 $self->d_reject($reason);
194 32         94 $self->d_traverse;
195             }
196              
197 156 50   156 0 450 sub d_resolve_continue ($self, $v) {
  156 50       370  
  156         272  
  156         261  
  156         254  
198 156         425 $self->d_resolve($v);
199 156 100       558 return unless $self->d_settled;
200 118         288 $self->d_traverse;
201             }
202              
203             # breadth-first
204 179 50   179 0 485 sub d_traverse($self) {
  179 50       413  
  179         290  
  179         262  
205              
206 179         412 my @stack = ($self);
207 179         472 while (@stack) {
208              
209 225         422 my $parent = shift @stack;
210              
211             #assert($parent->d_settled);
212 225 100       1233 my @children = $parent->d_children->@* or next;
213 124         284 $parent->{d_children} = [];
214              
215             # 2.2.2 - 2.2.7
216 124         491 my ($pstate, $v) = ($parent->state, $parent->d_v);
217 124         265 foreach my $cur (@children) {
218 128 100       429 my $h = $pstate eq FULFILLED ? $cur->d_fh : $cur->d_rh;
219 128         464 $cur->d_fh(undef)->d_rh(undef);
220              
221 128 100       342 if ($h) {
222             my $sub = sub {
223 81     81   2175 my $x;
224 81 100       158 eval { $x = $h->($v); 1 } ? $cur->d_resolve_continue($x) : $cur->d_reject_continue($@);
  81         201  
  75         448  
225 82         380 };
226 82         326 $self->postpone($sub); # 2.2.4, call async
227 82         1234 next;
228             }
229              
230 46 100       166 $pstate eq FULFILLED ? $cur->d_fulfill($v) : $cur->d_reject($v);
231 46         167 push @stack, $cur;
232             }
233              
234             }
235              
236             }
237              
238              
239             1;
240              
241             __END__