package Promises::Cookbook::TIMTOWTDI;

# ABSTRACT: Counter examples to Promises

__END__

=pod

=head1 NAME

Promises::Cookbook::TIMTOWTDI - Counter examples to Promises

=head1 VERSION

version 1.00

=head1 DESCRIPTION

So, like I said before, Promises are a means by which you can more
effectively manage your async operations and avoid callback spaghetti.
But of course this is Perl and therefore there is always another way
to do it. In this section I am going to show a few examples of other
ways you could accomplish the same thing.

=head2 Caveat

Please note that I am specifically illustrating ways to do this which
I feel are inferior or less elegant then Promises. This is not meant
to be a slight on the API of other modules at all, I am simply using
these modules to try and illustrate other (perhaps more familiar)
idioms in hopes that it will help people understand Promises.

I am sure there are other ways to do some of these things and do
them more effectively, and I am fully willing to admit my ignorance
here. I welcome any patches which might illustrate said ignorance, as
I do not claim at all to be an expert in async programming.

=head1 AnyEvent::HTTP

So, enough caveating, please consider this (more traditional) version
of our the L<Promises> SYNOPSIS example using L<AnyEvent::HTTP>.

  my $cv = AnyEvent->condvar;

  http_get('http://rest.api.example.com/-/product/12345', sub {
      my ($product) = @_;
      http_get('http://rest.api.example.com/-/product/suggestions?for_sku=12345', sub {
          my ($suggestions) = @_;
          http_get('http://rest.api.example.com/-/product/reviews?for_sku=12345', sub {
              my ($reviews) = @_;
              $cv->send({
                  product     => $product,
                  suggestions => $suggestions,
                  reviews     => $reviews,
              })
          }),
      });
  });

  my $all_product_info = $cv->recv;

Not only do we have deeply nested callbacks, but we have an enforced
order of operations. If you wanted to try and avoid that order of
operations, you might end up writing something like this:

   my $product_cv    = AnyEvent->condvar;
   my $suggestion_cv = AnyEvent->condvar;
   my $review_cv     = AnyEvent->condvar;

   http_get('http://rest.api.example.com/-/product/12345', sub {
       my ($product) = @_;
       $product_cv->send( $product );
   });

   http_get('http://rest.api.example.com/-/product/suggestions?for_sku=12345', sub {
       my ($suggestions) = @_;
       $suggestion_cv->send( $suggestions );
   });

   http_get('http://rest.api.example.com/-/product/reviews?for_sku=12345', sub {
       my ($reviews) = @_;
       $reviews_cv->send( $reviews )
   }),

   my $all_product_info = {
       product     => $product_cv->recv,
       suggestions => $suggestions_cv->recv,
       reviews     => $reviews_cv->recv
   };

But actually, this doesn't work either, while we do gain something by
allowing the C<http_get> calls to be run in whatever order works best,
we still end up still enforcing some order in the way we call C<recv>
on our three C<condvars> (Oh yeah, and we had to create and manage three
C<condvars> as well).

The following example was submitted to me by James Wright (via RT #83992)
as an alternate approach which is non-nested, uses only one condvar, and
has no fixed-order.

  my $cv = AnyEvent->condvar;
  my ( $product, $suggestions, $reviews ) = ( [], [], [] );

  $cv->begin;
  http_get('http://rest.api.example.com/-/product/12345', sub {
      ($product) = @_;
      $cv->end;
  });

  $cv->begin;
  http_get('http://rest.api.example.com/-/product/suggestions?for_sku=12345', sub {
      ($suggestions) = @_;
      $cv->end;
  });

  $cv->begin;
  http_get('http://rest.api.example.com/-/product/reviews?for_sku=12345', sub {
      ($reviews) = @_;
      $cv->end;
  });

  $cv->cb(sub {
      $cv->send({
          product     => $product,
          suggestions => $suggestions,
          reviews     => $reviews,
      });
  });

  my $all_product_info = $cv->recv;

The only real issue I have with this approach is the semi-global variable usage
(C<$product>, C<$suggestions> and C<$reviews>), but otherwise it works fine.

NOTE: Again, if you can think of a better way to do this that I missed,
please let me know.

=head1 Mojo::UserAgent

    #!/usr/bin/env perl

    use Mojo::Base -strict;
    use Mojo::UserAgent;

    my $titles;

    my $ua = Mojo::UserAgent->new;
    Mojo::IOLoop->delay(
        sub {
            my $delay = shift;
            $ua->get('http://google.com/', $delay->begin);
            $ua->get('http://yahoo.com/', $delay->begin);
            $ua->get('http://perlmonks.org/', $delay->begin);
        },
        sub {
            my ($delay, $tx1, $tx2, $tx3) = @_;
            $titles = {
                google      => $tx1->res->dom->at('title')->text,
                yahoo       => $tx2->res->dom->at('title')->text,
                perlmonks   => $tx3->res->dom->at('title')->text,
            };
        },
    )->catch(
        sub {
            my ($delay, $err) = @_;
            warn "failed to download or parse title\n";
        }
    )->wait;

    say Mojo::Util::dumper($titles);

=head1 AUTHOR

Stevan Little <stevan.little@iinteractive.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2019, 2017, 2014, 2012 by Infinity Interactive, Inc..

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
