File Coverage

blib/lib/Evo/Promise/Role.pm
Criterion Covered Total %
statement 196 199 98.4
branch 74 112 66.0
condition 2 3 66.6
subroutine 29 29 100.0
pod 0 17 0.0
total 301 360 83.6


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