File Coverage

blib/lib/Promise/Tiny.pm
Criterion Covered Total %
statement 100 102 98.0
branch 24 28 85.7
condition 4 9 44.4
subroutine 23 24 95.8
pod 0 8 0.0
total 151 171 88.3


line stmt bran cond sub pod time code
1             package Promise::Tiny;
2 8     8   171094 use 5.008001;
  8         25  
3 8     8   43 use strict;
  8         12  
  8         192  
4 8     8   34 use warnings;
  8         15  
  8         449  
5              
6             our $VERSION = "0.02";
7              
8 8     8   41 use Scalar::Util qw(blessed);
  8         14  
  8         920  
9 8     8   44 use Exporter qw(import);
  8         31  
  8         10711  
10              
11             our @EXPORT_OK = qw(promise);
12              
13             sub promise {
14 4     4 0 9195 return __PACKAGE__->new(@_);
15             }
16              
17             #### constructor ####
18              
19             sub new {
20 111     111 0 18317 my ($class, $code) = @_;
21 111         246 my $self = bless {}, $class;
22 111         153 eval {
23             $code->(
24 86     86   690061 sub { $self->_resolve(@_); },
25 11     11   99601 sub { $self->_reject(@_); },
26 111         617 );
27             };
28 111 100       10014 if ($@) {
29 6         36 $self->_reject($@);
30             }
31 111         482 return $self;
32             }
33              
34             #### instance methods ####
35              
36             sub _resolve {
37 86     86   123 my ($self, $value) = @_;
38 86 50 33     515 unless ($self->{_fulfilled} || $self->{_rejected}) {
39 86         157 $self->{_fulfilled} = 1;
40 86         189 $self->{_fulfilled_value} = $value;
41 86 100       493 if ($self->{on_fulfilled}) {
42 16         51 $self->{on_fulfilled}->($value);
43             }
44             }
45             }
46              
47             sub _reject {
48 17     17   41 my ($self, $reason) = @_;
49 17 50 33     111 unless ($self->{_fulfilled} || $self->{_rejected}) {
50 17         31 $self->{_rejected} = 1;
51 17         35 $self->{_rejected_reason} = $reason;
52 17 100       77 if ($self->{on_rejected}) {
53 3         9 $self->{on_rejected}->($reason);
54             }
55             }
56             }
57              
58             sub then {
59 61     61 0 106 my ($self, $on_fulfilled, $on_rejected) = @_;
60 61         99 my $class = ref $self;
61             return $class->new(sub {
62 61     61   86 my ($resolve, $reject) = @_;
63              
64             my $handler_wrapper = sub {
65 113         131 my ($handler) = @_;
66             return sub {
67 57         90 my ($value) = @_;
68 57         69 my $resolved_value = eval { $handler->($value); };
  57         154  
69 57 100       4855 if ($@) {
    100          
70 1         2 $reject->($@);
71             } elsif (_is_promise($resolved_value)) {
72             $resolved_value->then(sub {
73 2         5 my ($value) = @_;
74 2         11 $resolve->($value);
75             }, sub {
76 1         2 my ($reason) = @_;
77 1         3 $reject->($reason);
78 3         43 });
79             } else {
80 53         111 $resolve->($resolved_value);
81             }
82 113         663 };
83 61         213 };
84              
85 61 50       156 if ($on_fulfilled) {
86 61         113 $self->{on_fulfilled} = $handler_wrapper->($on_fulfilled);
87 61 100       192 if ($self->{_fulfilled}) {
88 24         56 $self->{on_fulfilled}->($self->{_fulfilled_value});
89             }
90             }
91 61 100       141 if ($on_rejected) {
92 52         91 $self->{on_rejected} = $handler_wrapper->($on_rejected);
93 52 100       161 if ($self->{_rejected}) {
94 14         33 $self->{on_rejected}->($self->{_rejected_reason});
95             }
96             }
97 61         314 });
98             }
99              
100             sub catch {
101 4     4 0 5 my ($self, $on_rejected) = @_;
102             return $self->then(
103             sub {
104 0     0   0 my ($value) = @_;
105 0         0 return $value;
106             },
107 4         20 $on_rejected
108             );
109             }
110              
111             #### static methods ####
112              
113             sub resolve {
114 5     5 0 969 my ($class, $value) = @_;
115              
116             return $class->new(sub {
117 5     5   11 my ($resolve, undef) = @_;
118 5         10 $resolve->($value);
119 5         33 });
120             }
121              
122             sub reject {
123 1     1 0 2085 my ($class, $reason) = @_;
124              
125             return $class->new(sub {
126 1     1   4 my (undef, $reject) = @_;
127 1         3 $reject->($reason);
128 1         12 });
129             }
130              
131             sub all {
132 5     5 0 1088 my ($class, $iterable) = @_;
133 5 100       10 my @promises = map { _is_promise($_) ? $_ : $class->resolve($_) } @$iterable;
  12         23  
134              
135             return $class->new(sub {
136 5     5   10 my ($resolve, $reject) = @_;
137 5         6 my $unresolved_size = scalar(@promises);
138 5         27 for my $promise (@promises) {
139             $promise->then(sub {
140 10         13 my ($value) = @_;
141 10         12 $unresolved_size--;
142 10 100       39 if ($unresolved_size <= 0) {
143 3         8 $resolve->([ map { $_->{_fulfilled_value} } @promises ]);
  8         26  
144             }
145             }, sub {
146 2         4 my ($reason) = @_;
147 2         4 $reject->($reason);
148 12         92 });
149             }
150 5         32 });
151             }
152              
153             sub race {
154 4     4 0 78 my ($class, $iterable) = @_;
155 4 50       6 my @promises = map { _is_promise($_) ? $_ : $class->resolve($_) } @$iterable;
  8         16  
156              
157             return $class->new(sub {
158 4     4   6 my ($resolve, $reject) = @_;
159 4         7 for my $promise (@promises) {
160             $promise->then(sub {
161 3         3 my ($value) = @_;
162 3         7 $resolve->($value);
163             }, sub {
164 1         2 my ($reason) = @_;
165 1         2 $reject->($reason);
166 8         51 });
167             }
168 4         22 });
169             }
170              
171             #### utility ####
172              
173             sub _is_promise {
174 76     76   98 my ($value) = @_;
175 76   66     534 return blessed $value && $value->isa(__PACKAGE__);
176             }
177              
178             1;
179              
180             __END__