File Coverage

blib/lib/AnyEvent/XSPromises.pm
Criterion Covered Total %
statement 38 39 97.4
branch 4 6 66.6
condition 1 3 33.3
subroutine 10 10 100.0
pod 3 3 100.0
total 56 61 91.8


line stmt bran cond sub pod time code
1             package AnyEvent::XSPromises;
2              
3 2     2   149162 use 5.010;
  2         18  
4 2     2   11 use strict;
  2         5  
  2         47  
5 2     2   9 use warnings;
  2         4  
  2         87  
6              
7 2     2   807 use AnyEvent::XSPromises::Loader;
  2         6  
  2         71  
8              
9 2     2   13 use Exporter 'import';
  2         3  
  2         708  
10             our @EXPORT_OK= qw/collect deferred resolved rejected/;
11              
12             sub resolved {
13 28     28 1 12337 my $d= deferred;
14 28         99 $d->resolve(@_);
15 28         323 return $d->promise;
16             }
17              
18             sub rejected {
19 7     7 1 144 my $d= deferred;
20 7         28 $d->reject(@_);
21 7         88 return $d->promise;
22             }
23              
24             # XXX This is pure-perl, not XS like we promise our users.
25             sub collect {
26 6     6 1 382 my $remaining= 0+@_;
27 6         11 my @values;
28 6         11 my $failed= 0;
29 6         17 my $then_what= deferred;
30 6         11 my $pending= 1;
31 6         11 my $i= 0;
32 6         14 for my $p (@_) {
33 51         78 my $i= $i++;
34             $p->then(sub {
35 50     50   27424 $values[$i]= [@_];
36 50 100       355 if ((--$remaining) == 0) {
37 5         11 $pending= 0;
38 5         83 $then_what->resolve(@values);
39             }
40             }, sub {
41 1 50   1   5 if (!$failed++) {
42 1         2 $pending= 0;
43 1         10 $then_what->reject(@_);
44             }
45 51         280 });
46             }
47 6 50 33     25 if (!$remaining && $pending) {
48 0         0 $then_what->resolve(@values);
49             }
50 6         97 return $then_what->promise;
51             }
52              
53             1;
54              
55             =head1 NAME
56              
57             AnyEvent::XSPromises - Another Promises library, this time implemented in XS for performance
58              
59             =head1 SYNOPSIS
60              
61             use AnyEvent::XSPromises qw/deferred/;
62             use AnyEvent::YACurl;
63              
64             sub do_request {
65             my $request_args= @_;
66              
67             my $deferred= deferred;
68             AnyEvent::YACurl->new({})->request(
69             sub {
70             my ($response, $error)= @_;
71             if ($error) { $deferred->reject($error); return; }
72             $deferred->resolve($response);
73             },
74             $request_args
75             );
76              
77             return $deferred->promise;
78             }
79              
80             =head1 DESCRIPTION
81              
82             This library provides a Promises interface, written in XS for performance, conforming to the Promises/A+ specification.
83              
84             Performance may not immediately seem important, but when promises are used as the building block for sending thousands
85             of database queries per second from a single Perl process, those extra microseconds suddenly start to matter.
86              
87             =head1 API
88              
89             =head2 AnyEvent::XSPromises
90              
91             =over
92              
93             =item deferred()
94              
95             C is the main entry point for using promises. This function will return a Deferred Object that must be
96             resolved or rejected after some event completes.
97              
98             sub get_perl {
99             my $d= deferred;
100             http_get("https://perl.org", sub {
101             $d->resolve(@_);
102             });
103             return $d->promise;
104             }
105              
106             =item collect(...)
107              
108             C makes a promise out of a collection of other promises (thenables). If all inputs get resolved, the promise will
109             be resolved with the outputs of each. If any input gets rejected, the promise will be rejected with its reason.
110              
111             Because of how context (array vs scalar) works in Perl, all outputs are wrapped in an arrayref.
112              
113             collect(
114             resolved(1),
115             resolved(2)
116             )->then(sub {
117             # @_ is now ( [1], [2] )
118             })
119              
120             =item resolved(...)
121              
122             Shortcut for creating a promise that has been resolved with the given inputs
123              
124             resolved(5)->then(sub {
125             my $five= shift;
126             })
127              
128             =item rejected(...)
129              
130             Shortcut for creating a promise that has been rejected with the given inputs. See C
131              
132             =back
133              
134             =head2 Deferred objects
135              
136             =over
137              
138             =item $d->promise()
139              
140             Gets a thenable promise associated to the Deferred object.
141              
142             my $d= deferred;
143             ...
144             return $d->promise;
145              
146             =item $d->resolve(...)
147              
148             Resolves the deferred object (assigns a value). All associated promises will have their callback invoked in the next event
149             loop iteration.
150              
151             =item $d->reject(...)
152              
153             Rejects the deferred object (assigns a reason for why it failed). All associated promises will have their callback invoked
154             in the next event loop iteration.
155              
156             =item $d->is_in_progress()
157              
158             Returns true iff the C or C method has not been called yet. Useful for racing multiple code paths to
159             resolve/reject a single deferred object, like one would do to build a timeout.
160              
161             sub get_with_timeout {
162             my $d= deferred;
163             my $timer; $timer= AE::timer 1, 0, sub {
164             undef $timer;
165             $d->reject("Timed out") if $d->is_in_progress;
166             };
167              
168             http_get("https://perl.org", sub {
169             my $result= shift
170             $d->resolve($result) if $d->is_in_progress;
171             });
172              
173             This method is intentionally not available on promise objects.
174              
175             =back
176              
177             =head2 Promise objects
178              
179             =over
180              
181             =item $p->then($on_resolve, $on_reject)
182              
183             Registers the given C and/or C callback on the promise, and returns a new promise.
184              
185             =item $p->catch($on_reject)
186              
187             Similar to C, but only takes C.
188              
189             =item $p->finally($on_finally)
190              
191             Register a callback on the promise that will be invoked once it completes. The callback is quietly executed but cannot
192             change the output or status of the promise. Returns a promise that will be resolved/rejected based on the original promise.
193              
194             =back
195              
196             =head1 COMPARISON TO OTHER PROMISES LIBRARIES
197              
198             =over
199              
200             =item Promises
201              
202             L is a pure-Perl Promises implementation that allows selecting one of multiple event loop backends. However,
203             this backend is defined globally and the documentation suggests that it would be best if only the application developer
204             picks a backend. This means that libraries cannot know up front which event loop backend they have to use, and they need
205             to support all event loops or the library would break if a different event loop is chosen. This has lead library authors
206             to mandate that the selected backend is AnyEvent, defying the purpose of backend selection other than for usage in
207             scripts that do not need compatibility with other code such as libraries from CPAN.
208              
209             The library also trades performance and resilience for a few features that are not needed to implement the Promises/A+
210             specification.
211              
212             Promises from this library are compatible with ours if the backend is set to C or C.
213              
214             =item AnyEvent::Promises
215              
216             L is another pure-Perl Promises implementation. It is a lot simpler than L, but comes with
217             performance implications, and has not been very hardened against developer error. Since it is also based on AnyEvent,
218             and comes with an identical C<< then($on_resolve, $on_reject) >> API, its promises are fully compatible with ours and
219             can be freely passed around between the two libraries if necessary.
220              
221             =back
222              
223             =head1 AUTHOR
224              
225             Tom van der Woerdt