File Coverage

blib/lib/Promise/ES6.pm
Criterion Covered Total %
statement 95 97 97.9
branch 19 24 79.1
condition n/a
subroutine 18 19 94.7
pod 1 8 12.5
total 133 148 89.8


line stmt bran cond sub pod time code
1             package Promise::ES6;
2              
3 28     28   3040629 use strict;
  28         306  
  28         885  
4 28     28   156 use warnings;
  28         53  
  28         32509  
5              
6             our $VERSION = '0.02-TRIAL2';
7              
8             =encoding utf-8
9              
10             =head1 NAME
11              
12             Promise::ES6 - ES6-style promises in Perl
13              
14             =head1 SYNOPSIS
15              
16             my $promise = Promise::ES6->new( sub {
17             my ($resolve_cr, $reject_cr) = @_;
18              
19             # ..
20             } );
21              
22             my $promise2 = $promise->then( sub { .. }, sub { .. } );
23              
24             my $promise3 = $promise->catch( sub { .. } );
25              
26             my $promise4 = $promise->finally( sub { .. } );
27              
28             my $resolved = Promise::ES6->resolve(5);
29             my $rejected = Promise::ES6->reject('nono');
30              
31             my $all_promise = Promise::ES6->all( \@promises );
32              
33             my $race_promise = Promise::ES6->race( \@promises );
34              
35             =head1 DESCRIPTION
36              
37             This is a rewrite of L that implements fixes for
38             certain bugs that proved hard to fix in the original code. This module
39             also removes superfluous dependencies on L and L.
40              
41             The interface is the same, except:
42              
43             =over
44              
45             =item * Promise resolutions and rejections accept exactly one argument,
46             not a list. (This accords with the standard.)
47              
48             =item * A C method is defined.
49              
50             =back
51              
52             =head1 COMPATIBILITY
53              
54             Right now this doesn’t try for interoperability with other promise
55             classes. If that’s something you want, make a feature request.
56              
57             =head1 SEE ALSO
58              
59             If you’re not sure of what promises are, there are several good
60             introductions to the topic. You might start with
61             L.
62              
63             =cut
64              
65             sub new {
66 65     65 0 40827 my ($class, $cr) = @_;
67              
68 65         204 my $self = bless {}, $class;
69              
70 65     32   289 my $resolver = sub { $self->_finish( resolve => $_[0]) };
  32         965470  
71 65     10   300 my $rejecter = sub { $self->_finish( reject => $_[0]) };
  10         112795  
72              
73 65         126 local $@;
74 65 100       126 if ( !eval { $cr->( $resolver, $rejecter ); 1 } ) {
  65         201  
  59         387  
75 6         63 $self->_finish( reject => $@ );
76             }
77              
78 65         448 return $self;
79             }
80              
81             sub then {
82 70     70 0 11236 my ($self, $on_resolve, $on_reject) = @_;
83              
84 70         349 my $new = {
85             _on_resolve => $on_resolve,
86             _on_reject => $on_reject,
87             };
88              
89 70         282 bless $new, (ref $self);
90              
91 70 100       271 if ($self->{'_finished_how'}) {
92 40         97 $new->_finish( $self->{'_finished_how'} => $self->{'_value'} );
93             }
94             else {
95 30         60 push @{ $self->{'_dependents'} }, $new;
  30         182  
96             }
97              
98 70         273 return $new;
99             }
100              
101 6     6 0 27 sub catch { return $_[0]->then( undef, $_[1] ) }
102              
103             sub finally {
104 0     0 1 0 my ($self, $todo_cr) = @_;
105              
106 0         0 return $self->then( $todo_cr, $todo_cr );
107             }
108              
109             sub _finish {
110 110     110   449 my ($self, $how, $value) = @_;
111              
112 110 50       423 die "$self already finished!" if $self->{'_finished_how'};
113              
114 110         237 local $@;
115              
116 110 100       454 if ($self->{"_on_$how"}) {
117 61 100       207 if ( eval { $value = $self->{"_on_$how"}->($value); 1 } ) {
  61         224  
  60         3296  
118 60         135 $how = 'resolve';
119             }
120             else {
121 1         7 $how = 'reject';
122 1         1 $value = $@;
123             }
124             }
125              
126 110         228 my $repromise_if_needed;
127             $repromise_if_needed = sub {
128 113     113   333 my ($repromise_how, $repromise_value) = @_;
129              
130 113 100       252 if (eval { $repromise_value->isa(__PACKAGE__) }) {
  113         1066  
131 3         10 $self->{'_unresolved_value'} = $repromise_value;
132              
133             $repromise_value->then(
134 2         12 sub { $repromise_if_needed->( resolve => $_[0]) },
135             sub {
136 1         4 $repromise_if_needed->( reject => $_[0]);
137             },
138 3         46 );
139             }
140             else {
141 110         520 $self->{'_value'} = $repromise_value;
142 110         405 $self->{'_finished_how'} = $repromise_how;
143              
144 110         231 $_->_finish($repromise_how, $repromise_value) for @{ $self->{'_dependents'} };
  110         509  
145             }
146 110         724 };
147              
148 110         390 $repromise_if_needed->($how, $value);
149              
150 110         331 return;
151             }
152              
153             #----------------------------------------------------------------------
154              
155             sub resolve {
156 5     5 0 1634 my ($class, $value) = @_;
157              
158             return $class->new(sub {
159 5     5   15 my ($resolve, undef) = @_;
160 5         62 $resolve->($value);
161 5         85 });
162             }
163              
164             sub reject {
165 1     1 0 2230 my ($class, $reason) = @_;
166              
167             return $class->new(sub {
168 1     1   3 my (undef, $reject) = @_;
169 1         4 $reject->($reason);
170 1         10 });
171             }
172              
173             sub all {
174 5     5 0 2535 my ($class, $iterable) = @_;
175 5 100       44 my @promises = map { _is_promise($_) ? $_ : $class->resolve($_) } @$iterable;
  12         51  
176              
177             return $class->new(sub {
178 5     5   8 my ($resolve, $reject) = @_;
179 5         10 my $unresolved_size = scalar(@promises);
180 5         9 for my $promise (@promises) {
181             $promise->then(sub {
182 10         19 my ($value) = @_;
183 10         24 $unresolved_size--;
184 10 100       45 if ($unresolved_size <= 0) {
185 3         10 $resolve->([ map { $_->{_value} } @promises ]);
  8         36  
186             }
187             }, sub {
188 2         3 my ($reason) = @_;
189 2         5 $reject->($reason);
190 12         103 });
191             }
192 5         36 });
193             }
194              
195             sub race {
196 4     4 0 2657 my ($class, $iterable) = @_;
197 4 50       95 my @promises = map { _is_promise($_) ? $_ : $class->resolve($_) } @$iterable;
  8         92  
198              
199             return $class->new(sub {
200 4     4   33 my ($resolve, $reject) = @_;
201              
202 4         10 my $is_done;
203              
204 4         23 for my $promise (@promises) {
205 8 50       79 last if $is_done;
206              
207             $promise->then(sub {
208 3         9 my ($value) = @_;
209              
210 3 50       9 return if $is_done;
211 3         6 $is_done = 1;
212              
213 3         7 $resolve->($value);
214             }, sub {
215 1         1 my ($reason) = @_;
216              
217 1 50       4 return if $is_done;
218 1         1 $is_done = 1;
219              
220 1         3 $reject->($reason);
221 8         189 });
222             }
223 4         150 });
224             }
225              
226             sub _is_promise {
227 20     20   64 local $@;
228 20         97 return eval { $_[0]->isa(__PACKAGE__) };
  20         446  
229             }
230              
231             1;