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 19     19   2386648 use strict;
  19         183  
  19         574  
4 19     19   99 use warnings;
  19         36  
  19         22648  
5              
6             our $VERSION = '0.02-TRIAL1';
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 88     88 0 2565414 my ($class, $cr) = @_;
67              
68 88         314 my $self = bless {}, $class;
69              
70 88     48   420 my $resolver = sub { $self->_finish( resolve => $_[0]) };
  48         1515999  
71 88     13   418 my $rejecter = sub { $self->_finish( reject => $_[0]) };
  13         132717  
72              
73 88         178 local $@;
74 88 100       188 if ( !eval { $cr->( $resolver, $rejecter ); 1 } ) {
  88         282  
  81         655  
75 7         166 $self->_finish( reject => $@ );
76             }
77              
78 88         707 return $self;
79             }
80              
81             sub then {
82 96     96 0 18581 my ($self, $on_resolve, $on_reject) = @_;
83              
84 96         494 my $new = {
85             _on_resolve => $on_resolve,
86             _on_reject => $on_reject,
87             };
88              
89 96         357 bless $new, (ref $self);
90              
91 96 100       302 if ($self->{'_finished_how'}) {
92 52         184 $new->_finish( $self->{'_finished_how'} => $self->{'_value'} );
93             }
94             else {
95 44         201 push @{ $self->{'_dependents'} }, $new;
  44         477  
96             }
97              
98 96         311 return $new;
99             }
100              
101 6     6 0 32 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 153     153   537 my ($self, $how, $value) = @_;
111              
112 153 50       488 die "$self already finished!" if $self->{'_finished_how'};
113              
114 153         262 local $@;
115              
116 153 100       517 if ($self->{"_on_$how"}) {
117 84 100       180 if ( eval { $value = $self->{"_on_$how"}->($value); 1 } ) {
  84         292  
  83         4810  
118 83         153 $how = 'resolve';
119             }
120             else {
121 1         12 $how = 'reject';
122 1         2 $value = $@;
123             }
124             }
125              
126 153         249 my $repromise_if_needed;
127             $repromise_if_needed = sub {
128 157     157   406 my ($repromise_how, $repromise_value) = @_;
129              
130 157 100       335 if (eval { $repromise_value->isa(__PACKAGE__) }) {
  157         1331  
131 4         65 $self->{'_unresolved_value'} = $repromise_value;
132              
133             $repromise_value->then(
134 3         25 sub { $repromise_if_needed->( resolve => $_[0]) },
135             sub {
136 1         5 $repromise_if_needed->( reject => $_[0]);
137             },
138 4         108 );
139             }
140             else {
141 153         400 $self->{'_value'} = $repromise_value;
142 153         338 $self->{'_finished_how'} = $repromise_how;
143              
144 153         251 $_->_finish($repromise_how, $repromise_value) for @{ $self->{'_dependents'} };
  153         621  
145             }
146 153         1112 };
147              
148 153         491 $repromise_if_needed->($how, $value);
149              
150 153         488 return;
151             }
152              
153             #----------------------------------------------------------------------
154              
155             sub resolve {
156 6     6 0 1689 my ($class, $value) = @_;
157              
158             return $class->new(sub {
159 6     6   14 my ($resolve, undef) = @_;
160 6         13 $resolve->($value);
161 6         77 });
162             }
163              
164             sub reject {
165 1     1 0 1513 my ($class, $reason) = @_;
166              
167             return $class->new(sub {
168 1     1   2 my (undef, $reject) = @_;
169 1         3 $reject->($reason);
170 1         8 });
171             }
172              
173             sub all {
174 6     6 0 3145 my ($class, $iterable) = @_;
175 6 100       47 my @promises = map { _is_promise($_) ? $_ : $class->resolve($_) } @$iterable;
  15         50  
176              
177             return $class->new(sub {
178 6     6   19 my ($resolve, $reject) = @_;
179 6         12 my $unresolved_size = scalar(@promises);
180 6         14 for my $promise (@promises) {
181             $promise->then(sub {
182 13         25 my ($value) = @_;
183 13         18 $unresolved_size--;
184 13 100       43 if ($unresolved_size <= 0) {
185 4         9 $resolve->([ map { $_->{_value} } @promises ]);
  11         31  
186             }
187             }, sub {
188 2         12 my ($reason) = @_;
189 2         7 $reject->($reason);
190 15         94 });
191             }
192 6         50 });
193             }
194              
195             sub race {
196 7     7 0 6796 my ($class, $iterable) = @_;
197 7 50       236 my @promises = map { _is_promise($_) ? $_ : $class->resolve($_) } @$iterable;
  14         96  
198              
199             return $class->new(sub {
200 7     7   45 my ($resolve, $reject) = @_;
201              
202 7         39 my $is_done;
203              
204 7         54 for my $promise (@promises) {
205 14 50       86 last if $is_done;
206              
207             $promise->then(sub {
208 5         15 my ($value) = @_;
209              
210 5 50       15 return if $is_done;
211 5         10 $is_done = 1;
212              
213 5         12 $resolve->($value);
214             }, sub {
215 2         18 my ($reason) = @_;
216              
217 2 50       48 return if $is_done;
218 2         6 $is_done = 1;
219              
220 2         24 $reject->($reason);
221 14         266 });
222             }
223 7         545 });
224             }
225              
226             sub _is_promise {
227 29     29   124 local $@;
228 29         137 return eval { $_[0]->isa(__PACKAGE__) };
  29         511  
229             }
230              
231             1;