File Coverage

blib/lib/Promise/ES6.pm
Criterion Covered Total %
statement 89 91 97.8
branch 16 18 88.8
condition n/a
subroutine 18 19 94.7
pod 1 8 12.5
total 124 136 91.1


line stmt bran cond sub pod time code
1             package Promise::ES6;
2              
3 16     16   1397803 use strict;
  16         138  
  16         462  
4 16     16   93 use warnings;
  16         36  
  16         18056  
5              
6             our $VERSION = '0.01-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 take 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 83     83 0 96632 my ($class, $cr) = @_;
67              
68 83         264 my $self = bless {}, $class;
69              
70 83     50   395 my $resolver = sub { $self->_finish( resolve => $_[0]) };
  50         1498110  
71 83     13   428 my $rejecter = sub { $self->_finish( reject => $_[0]) };
  13         102049  
72              
73 83         211 local $@;
74 83 100       150 if ( !eval { $cr->( $resolver, $rejecter ); 1 } ) {
  83         223  
  74         3272  
75 7         152 $self->_finish( reject => $@ );
76             }
77              
78 81         986 return $self;
79             }
80              
81             sub then {
82 93     93 0 13818 my ($self, $on_resolve, $on_reject) = @_;
83              
84 93         390 my $new = {
85             _on_resolve => $on_resolve,
86             _on_reject => $on_reject,
87             };
88              
89 93         375 bless $new, (ref $self);
90              
91 93 100       293 if ($self->{'_finished_how'}) {
92 66         182 $new->_finish( $self->{'_finished_how'} => $self->{'_value'} );
93             }
94             else {
95 27         105 push @{ $self->{'_dependents'} }, $new;
  27         151  
96             }
97              
98 93         254 return $new;
99             }
100              
101 5     5 0 48 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 154     154   527 my ($self, $how, $value) = @_;
111              
112 154 50       513 die "$self already finished!" if $self->{'_finished'};
113              
114 154         251 local $@;
115              
116 154 100       503 if ($self->{"_on_$how"}) {
117 83 100       190 if ( eval { $value = $self->{"_on_$how"}->($value); 1 } ) {
  83         325  
  82         4777  
118 82         230 $how = 'resolve';
119             }
120             else {
121 1         10 $how = 'reject';
122 1         2 $value = $@;
123             }
124             }
125              
126 154         240 my $repromise_if_needed;
127             $repromise_if_needed = sub {
128 158     158   384 my ($repromise_how, $repromise_value) = @_;
129              
130 158 100       287 if (eval { $repromise_value->isa(__PACKAGE__) }) {
  158         1284  
131 4         44 $self->{'_unresolved_value'} = $repromise_value;
132              
133             $repromise_value->then(
134 3         83 sub { $repromise_if_needed->( resolve => $_[0]) },
135             sub {
136 1         5 $repromise_if_needed->( reject => $_[0]);
137             },
138 4         147 );
139             }
140             else {
141 154         797 $self->{'_value'} = $repromise_value;
142 154         364 $self->{'_finished_how'} = $repromise_how;
143              
144 154         278 $_->_finish($repromise_how, $repromise_value) for @{ $self->{'_dependents'} };
  154         613  
145             }
146 154         1047 };
147              
148 154         522 $repromise_if_needed->($how, $value);
149              
150 154         900 return;
151             }
152              
153             #----------------------------------------------------------------------
154              
155             sub resolve {
156 6     6 0 1748 my ($class, $value) = @_;
157              
158             return $class->new(sub {
159 6     6   22 my ($resolve, undef) = @_;
160 6         13 $resolve->($value);
161 6         71 });
162             }
163              
164             sub reject {
165 1     1 0 1707 my ($class, $reason) = @_;
166              
167             return $class->new(sub {
168 1     1   3 my (undef, $reject) = @_;
169 1         2 $reject->($reason);
170 1         10 });
171             }
172              
173             sub all {
174 6     6 0 2228 my ($class, $iterable) = @_;
175 6 100       38 my @promises = map { _is_promise($_) ? $_ : $class->resolve($_) } @$iterable;
  15         55  
176              
177             return $class->new(sub {
178 6     6   18 my ($resolve, $reject) = @_;
179 6         11 my $unresolved_size = scalar(@promises);
180 6         13 for my $promise (@promises) {
181             $promise->then(sub {
182 13         25 my ($value) = @_;
183 13         18 $unresolved_size--;
184 13 100       46 if ($unresolved_size <= 0) {
185 4         10 $resolve->([ map { $_->{_value} } @promises ]);
  11         30  
186             }
187             }, sub {
188 2         4 my ($reason) = @_;
189 2         9 $reject->($reason);
190 15         142 });
191             }
192 6         69 });
193             }
194              
195             sub race {
196 7     7 0 4152 my ($class, $iterable) = @_;
197 7 50       150 my @promises = map { _is_promise($_) ? $_ : $class->resolve($_) } @$iterable;
  14         104  
198              
199             return $class->new(sub {
200 7     7   18 my ($resolve, $reject) = @_;
201 7         89 for my $promise (@promises) {
202             $promise->then(sub {
203 6         15 my ($value) = @_;
204 6         9 $resolve->($value);
205             }, sub {
206 2         18 my ($reason) = @_;
207 2         26 $reject->($reason);
208 14         218 });
209             }
210 7         341 });
211             }
212              
213             sub _is_promise {
214 29     29   48 local $@;
215 29         89 return eval { $_[0]->isa(__PACKAGE__) };
  29         399  
216             }
217              
218             1;