1Promises::Cookbook::TIMUTsOeWrTDCIo(n3t)ributed Perl DocPurmoemnitsaetsi:o:nCookbook::TIMTOWTDI(3)
2
3
4
6 Promises::Cookbook::TIMTOWTDI - Counter examples to Promises
7
9 version 1.04
10
12 So, like I said before, Promises are a means by which you can more
13 effectively manage your async operations and avoid callback spaghetti.
14 But of course this is Perl and therefore there is always another way to
15 do it. In this section I am going to show a few examples of other ways
16 you could accomplish the same thing.
17
18 Caveat
19 Please note that I am specifically illustrating ways to do this which I
20 feel are inferior or less elegant then Promises. This is not meant to
21 be a slight on the API of other modules at all, I am simply using these
22 modules to try and illustrate other (perhaps more familiar) idioms in
23 hopes that it will help people understand Promises.
24
25 I am sure there are other ways to do some of these things and do them
26 more effectively, and I am fully willing to admit my ignorance here. I
27 welcome any patches which might illustrate said ignorance, as I do not
28 claim at all to be an expert in async programming.
29
31 So, enough caveating, please consider this (more traditional) version
32 of our the Promises SYNOPSIS example using AnyEvent::HTTP.
33
34 my $cv = AnyEvent->condvar;
35
36 http_get('http://rest.api.example.com/-/product/12345', sub {
37 my ($product) = @_;
38 http_get('http://rest.api.example.com/-/product/suggestions?for_sku=12345', sub {
39 my ($suggestions) = @_;
40 http_get('http://rest.api.example.com/-/product/reviews?for_sku=12345', sub {
41 my ($reviews) = @_;
42 $cv->send({
43 product => $product,
44 suggestions => $suggestions,
45 reviews => $reviews,
46 })
47 }),
48 });
49 });
50
51 my $all_product_info = $cv->recv;
52
53 Not only do we have deeply nested callbacks, but we have an enforced
54 order of operations. If you wanted to try and avoid that order of
55 operations, you might end up writing something like this:
56
57 my $product_cv = AnyEvent->condvar;
58 my $suggestion_cv = AnyEvent->condvar;
59 my $review_cv = AnyEvent->condvar;
60
61 http_get('http://rest.api.example.com/-/product/12345', sub {
62 my ($product) = @_;
63 $product_cv->send( $product );
64 });
65
66 http_get('http://rest.api.example.com/-/product/suggestions?for_sku=12345', sub {
67 my ($suggestions) = @_;
68 $suggestion_cv->send( $suggestions );
69 });
70
71 http_get('http://rest.api.example.com/-/product/reviews?for_sku=12345', sub {
72 my ($reviews) = @_;
73 $reviews_cv->send( $reviews )
74 }),
75
76 my $all_product_info = {
77 product => $product_cv->recv,
78 suggestions => $suggestions_cv->recv,
79 reviews => $reviews_cv->recv
80 };
81
82 But actually, this doesn't work either, while we do gain something by
83 allowing the "http_get" calls to be run in whatever order works best,
84 we still end up still enforcing some order in the way we call "recv" on
85 our three "condvars" (Oh yeah, and we had to create and manage three
86 "condvars" as well).
87
88 The following example was submitted to me by James Wright (via RT
89 #83992) as an alternate approach which is non-nested, uses only one
90 condvar, and has no fixed-order.
91
92 my $cv = AnyEvent->condvar;
93 my ( $product, $suggestions, $reviews ) = ( [], [], [] );
94
95 $cv->begin;
96 http_get('http://rest.api.example.com/-/product/12345', sub {
97 ($product) = @_;
98 $cv->end;
99 });
100
101 $cv->begin;
102 http_get('http://rest.api.example.com/-/product/suggestions?for_sku=12345', sub {
103 ($suggestions) = @_;
104 $cv->end;
105 });
106
107 $cv->begin;
108 http_get('http://rest.api.example.com/-/product/reviews?for_sku=12345', sub {
109 ($reviews) = @_;
110 $cv->end;
111 });
112
113 $cv->cb(sub {
114 $cv->send({
115 product => $product,
116 suggestions => $suggestions,
117 reviews => $reviews,
118 });
119 });
120
121 my $all_product_info = $cv->recv;
122
123 The only real issue I have with this approach is the semi-global
124 variable usage ($product, $suggestions and $reviews), but otherwise it
125 works fine.
126
127 NOTE: Again, if you can think of a better way to do this that I missed,
128 please let me know.
129
131 #!/usr/bin/env perl
132
133 use Mojo::Base -strict;
134 use Mojo::UserAgent;
135
136 my $titles;
137
138 my $ua = Mojo::UserAgent->new;
139 Mojo::IOLoop->delay(
140 sub {
141 my $delay = shift;
142 $ua->get('http://google.com/', $delay->begin);
143 $ua->get('http://yahoo.com/', $delay->begin);
144 $ua->get('http://perlmonks.org/', $delay->begin);
145 },
146 sub {
147 my ($delay, $tx1, $tx2, $tx3) = @_;
148 $titles = {
149 google => $tx1->res->dom->at('title')->text,
150 yahoo => $tx2->res->dom->at('title')->text,
151 perlmonks => $tx3->res->dom->at('title')->text,
152 };
153 },
154 )->catch(
155 sub {
156 my ($delay, $err) = @_;
157 warn "failed to download or parse title\n";
158 }
159 )->wait;
160
161 say Mojo::Util::dumper($titles);
162
164 Stevan Little <stevan.little@iinteractive.com>
165
167 This software is copyright (c) 2020, 2019, 2017, 2014, 2012 by Infinity
168 Interactive, Inc.
169
170 This is free software; you can redistribute it and/or modify it under
171 the same terms as the Perl 5 programming language system itself.
172
173
174
175perl v5.32.0 2020-07-28 Promises::Cookbook::TIMTOWTDI(3)