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   5695 use Evo -Class;
  11         27  
  11         85  
3 11     11   76 use Evo '-Promise::Sync; -Lib try; -Promise::Const *; -Promise::Deferred';
  11         25  
  11         39  
4 11     11   89 use Evo 'Carp croak; Scalar::Util blessed';
  11         27  
  11         45  
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 1270 sub promise ($me, $fn) {
  2 50       10  
  2         7  
  2         5  
  2         5  
28 2         32 my $d = Evo::Promise::Deferred->new(promise => my $p = $me->new());
29             try {
30 2     2   19 $fn->(sub { $d->resolve(@_) }, sub { $d->reject(@_) });
  1         23  
  0         0  
31             }
32 1 50   1   15 sub($e) {
  1 50       5  
  1         4  
  1         3  
33 1         6 $d->reject(@_);
34 2         32 };
35 2         45 $p;
36             }
37              
38 1 50   1 0 405 sub deferred($me) { Evo::Promise::Deferred->new(promise => $me->new()); }
  1 50       5  
  1         2  
  1         2  
  1         8  
39              
40 9 50   9 0 2586 sub resolve ($me, $v) {
  9 50       24  
  9         17  
  9         14  
  9         12  
41 9         59 my $d = Evo::Promise::Deferred->new(promise => $me->new());
42 9         38 $d->resolve($v);
43 9         60 $d->promise;
44             }
45              
46 10 50   10 0 1753 sub reject ($me, $v) {
  10 50       24  
  10         17  
  10         15  
  10         29  
47 10         54 my $d = Evo::Promise::Deferred->new(promise => $me->new());
48 10         38 $d->reject($v);
49 10         46 $d->promise;
50             }
51              
52 7 50   7 0 68 sub race ($me, @prms) {
  7         15  
  7         24  
  7         12  
53 7         35 my $d = Evo::Promise::Deferred->new(promise => $me->new());
54 7     10   28 my $onF = sub { $d->resolve(@_) };
  10         32  
55 7     6   18 my $onR = sub { $d->reject(@_) };
  6         18  
56 7         17 foreach my $cur (@prms) {
57 17 50       45 if (ref $cur eq 'Evo::Promise::Class') {
58 0         0 $cur->then($onF, $onR);
59             }
60             else {
61             # wrap with our promise
62 17         74 my $wd = Evo::Promise::Deferred->new(promise => $me->new());
63 17         66 $wd->promise->then($onF, $onR);
64 17         49 $wd->resolve($cur);
65             }
66             }
67              
68 7         50 $d->promise;
69             }
70              
71              
72 6 50   6 0 81 sub all ($me, @prms) {
  6         10  
  6         24  
  6         13  
73 6         32 my $d = Evo::Promise::Deferred->new(promise => $me->new());
74 6 100       17 do { $d->resolve([]); return $d->promise; } unless @prms;
  1         8  
  1         20  
75              
76 5         8 my $pending = @prms;
77              
78 5         9 my @result;
79 5     6   16 my $onR = sub { $d->reject($_[0]) };
  6         18  
80              
81 5         19 for (my $i = 0; $i < @prms; $i++) {
82 20         46 my $cur_i = $i;
83 20         36 my $cur_p = $prms[$cur_i];
84 20 100   14   66 my $onF = sub { $result[$cur_i] = $_[0]; $d->resolve(\@result) if --$pending == 0; };
  14         27  
  14         46  
85              
86 20 50       52 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         98 my $wd = Evo::Promise::Deferred->new(promise => $me->new());
92 20         67 $wd->promise->then($onF, $onR);
93 20         51 $wd->resolve($cur_p);
94             }
95             }
96 5         44 $d->promise;
97             }
98              
99             ### OBJECT METHODS
100              
101 8 50   8 0 70 sub finally ($self, $fn) {
  8 50       19  
  8         12  
  8         12  
  8         11  
102 8         32 my $d = Evo::Promise::Deferred->new(promise => ref($self)->new);
103 8         15 my $me = ref($self);
104 4 50   4   9 my $onF = sub($v) {
  4 50       9  
  4         6  
  4         7  
105 4         10 $d->resolve($fn->()); # need pass result because it can be a promise
106 3         15 $d->promise->then(sub {$v});
  2         3  
107 8         26 };
108 4 50   4   12 my $onR = sub($r) {
  4 50       9  
  4         7  
  4         5  
109 4         8 $d->resolve($fn->()); # see above
110 3         15 $d->promise->then(sub { $me->reject($r) });
  2         5  
111 8         21 };
112 8         20 $self->then($onF, $onR);
113             }
114              
115 9 50   9 0 29 sub catch ($self, $cfn) {
  9 50       26  
  9         14  
  9         16  
  9         15  
116 9         23 $self->then(undef, $cfn);
117             }
118              
119 2 50   2 0 23 sub spread ($self, $fn) {
  2 50       14  
  2         7  
  2         5  
  2         5  
120 2 50   2   51 $self->then(sub($ref) { $fn->($ref->@*) });
  2 50       15  
  2         10  
  2         5  
  2         6  
  2         8  
121             }
122              
123              
124             sub then {
125 84     84 0 1111 my ($self, $fh, $rh) = @_;
126 84 100       597 my $p = ref($self)->new(ref($fh) ? (d_fh => $fh) : (), ref($rh) ? (d_rh => $rh) : ());
    100          
127 84         218 push $self->d_children->@*, $p;
128 84 100       256 $self->d_traverse if $self->d_settled;
129 84         245 $p;
130             }
131              
132             ### DRIVER INTERNAL METHODS
133              
134 36 50   36 0 93 sub d_lock_in ($self, $parent) {
  36 50       84  
  36         55  
  36         59  
  36         51  
135              
136             #assert(!$self->d_locked);
137             #assert(!$self->d_settled);
138 36         137 unshift $parent->d_children->@*, $self->d_locked(1);
139             }
140              
141 155 50   155 0 391 sub d_fulfill ($self, $v) {
  155 50       340  
  155         247  
  155         232  
  155         210  
142              
143             #assert(!$self->d_settled);
144 155         685 $self->d_settled(1)->state(FULFILLED)->d_v($v);
145             }
146              
147 67 50   67 0 173 sub d_reject ($self, $r) {
  67 50       170  
  67         118  
  67         104  
  67         98  
148              
149             #assert(!$_[0]->d_settled);
150 67         294 $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 160 50   160 0 384 sub d_resolve ($self, $x) {
  160 50       320  
  160         235  
  160         240  
  160         222  
157              
158             #assert(!$self->d_settled);
159              
160 160         233 while (1) {
161              
162             # 2.3.4 but means not a blessed object
163 164 100       606 return $self->d_fulfill($x) unless blessed($x);
164              
165              
166             # 2.3.1
167 57 100 66     329 return $self->d_reject('TypeError') if $x && $self eq $x;
168              
169             # 2.3.2 promise
170 56 100       155 if (ref $x eq ref $self) {
171 45 100       196 $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 45         99 return;
177             }
178              
179 11 100       56 if ($x->can('then')) {
180 10         97 my $sync = Evo::Promise::Sync->new(promise => $self)->try_thenable($x);
181 10 100       43 return unless $sync->should_resolve;
182 4         13 $x = $sync->v; # and next, but it's already last in loop
183 4         17 next;
184             }
185              
186             # 2.3.3.4
187 1         3 return $self->d_fulfill($x);
188             }
189             }
190              
191             # reject promise and call traverse with the stack of children
192 32 50   32 0 145 sub d_reject_continue ($self, $reason) {
  32 50       78  
  32         53  
  32         52  
  32         52  
193 32         87 $self->d_reject($reason);
194 32         82 $self->d_traverse;
195             }
196              
197 151 50   151 0 362 sub d_resolve_continue ($self, $v) {
  151 50       302  
  151         223  
  151         219  
  151         209  
198 151         348 $self->d_resolve($v);
199 151 100       485 return unless $self->d_settled;
200 114         234 $self->d_traverse;
201             }
202              
203             # breadth-first
204 173 50   173 0 417 sub d_traverse($self) {
  173 50       372  
  173         259  
  173         230  
205              
206 173         318 my @stack = ($self);
207 173         422 while (@stack) {
208              
209 218         367 my $parent = shift @stack;
210              
211             #assert($parent->d_settled);
212 218 100       1026 my @children = $parent->d_children->@* or next;
213 120         293 $parent->{d_children} = [];
214              
215             # 2.2.2 - 2.2.7
216 120         420 my ($pstate, $v) = ($parent->state, $parent->d_v);
217 120         216 foreach my $cur (@children) {
218 124 100       346 my $h = $pstate eq FULFILLED ? $cur->d_fh : $cur->d_rh;
219 124         390 $cur->d_fh(undef)->d_rh(undef);
220              
221 124 100       311 if ($h) {
222             my $sub = sub {
223 78     78   2777 my $x;
224 78 100       143 eval { $x = $h->($v); 1 } ? $cur->d_resolve_continue($x) : $cur->d_reject_continue($@);
  78         168  
  72         348  
225 79         305 };
226 79         556 $self->postpone($sub); # 2.2.4, call async
227 79         1049 next;
228             }
229              
230 45 100       142 $pstate eq FULFILLED ? $cur->d_fulfill($v) : $cur->d_reject($v);
231 45         140 push @stack, $cur;
232             }
233              
234             }
235              
236             }
237              
238              
239             1;
240              
241             __END__