NAME

Mojo::Promise::Role::Repeat - Promise looping construct with break

SYNOPSIS

# stupidly complicated while loop
Mojo::Promise->with_roles('+Repeat')->repeat( 5, sub {
    my $n = shift;
    $_->('yay') unless $n > 0;
    print "($n)";
    return $n-1;
})->then( sub { print @_; } )->wait
#
# (5)(4)(3)(2)(1)yay

# web treasure hunt pattern
my @clues;
$ua->get_p('http://example.com/start_here.html')->with_roles('+Repeat')
->repeat( sub {
   my $res = shift->result;
   die $res->message if $res->is_error;
   push @clues, $res->dom->at('#found_clue')->all_text;
   $_->()
     unless my $next_url = $res->dom->at('a#go_here_next')->{href};
   $ua->get_p($next_url)
})->then( sub {
   # do stuff with @clues
   ...
 },sub {
   # error handling
   ...
})

DESCRIPTION

Mojo::Promise::Role::Repeat, a role intended for Mojo::Promise objects, provides a looping construct for control flows involving promises and a "break" function that can escape through arbitrarily many levels of nested loops.

METHODS

Mojo::Promise::Role::Repeat supplies the following methods to the host object/class:

repeat

$done = Mojo::Promise->with_roles('+Repeat')
        ->repeat(@initial, sub {...});
$done = $promise
        ->repeat(@initial, sub {...});

The first form is equivalent to

$done = Mojo::Promise->resolve(@initial)
        ->then{sub {...})
        ->then(sub {...})
        ->then(sub {...})
        # ... forever

The second form is equivalent to

$done = $promise->then( sub { (@initial, @_) } )
        ->then(sub {...})
        ->then(sub {...})
        ->then(sub {...})
        # ... forever

In both cases, the value returned is effectily the promise generated by the "last" then call, the effect being to invoke the handler (sub {...}) repeatedly, each time passing the values returned from the previous iteration, and with $_ bound to a "break" function that, when called, does not return but instead exits the handler, abandons the loop, and resolves that final promise ($done) with the arguments provided.

If the "break" function is invoked with a promise passed as its first argument, the handler and loop are likewise abandoned, and the final promise awaits resolution/rejection of the passed promise.

If any iteration of the handler dies or returns a returns a promise that is rejected, the final promise is likewise rejected.

Note that the "break" function can be used to break out of nested handlers, but since $_ is dynamically bound and may change by the time a nested handler runs, it is highly recommended that, for nested handlers, you use a lexical variable to reference this function, e.g.,

$promise->repeat( sub {
    my $break = $_;
    ...
    $ua->get_p(...)->then( sub {
        ...
        $break->(@result) if (condition...);
        ...
    })->then( sub {
        ...
    })
})

Note that this method is EXPERIMENTAL and might change without warning!

SEE ALSO

Mojo::Promise, Mojolicious, Mojolicious::Guides, https://mojolicious.org.

AUTHOR

Roger Crew <wrog@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2019 by Roger Crew.

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