Async

A handler may hand back a future instead of a response: Punk awaits any future-compatible return (anything with then / on_ready / get). Punk::Future is the native one, implemented in C.

get '/slow' => sub {
    my ($c) = @_;
    $c->timer(2)->then(sub { $c->json({ waited => 2 }) });
};

On a Hyperman worker this answers two seconds later without pinning the worker - the future runs on the event loop and the worker serves other requests while it is pending. Anywhere else the same code blocks politely instead. One code path, no colored functions.

The primitives

my $f = $c->promise;               # a fresh Punk::Future
$f->done({ ok => 1 });             # resolve it
$f->fail('nope');                  # or fail it

$c->timer($secs);                  # a future that resolves after $secs
$c->await($f);                     # block (or park, on a loop) until done

And on any future:

$f->on_done(sub { my @v = @_; ... });
$f->on_fail(sub { my ($err) = @_; ... });
$f->then(sub { ... });             # chain; return a value or another future
$f->get;                           # the resolved value, awaiting if needed

With models

The async model backend returns futures from every call - see Models:

get '/report/:year' => sub {
    my ($c) = @_;
    $c->model('Report')->search({ year => $c->param('year') })
      ->then(sub { $c->json($_[0]) });
};

With outbound HTTP

$c->ua is future-based end to end:

get '/weather' => sub {
    my ($c) = @_;
    $c->ua->get('https://wttr.in/?format=j1')->then(sub {
        my ($res) = @_;
        $c->json({ upstream => $res->json });
    });
};

Fan-out

Futures compose; wait for several at once and combine:

get '/dashboard' => sub {
    my ($c) = @_;
    my $books  = $c->model('Book')->all;
    my $orders = $c->model('Order')->search({ open => 1 });
    $books->then(sub {
        my ($b) = @_;
        $orders->then(sub { $c->json({ books => $b, orders => $_[0] }) });
    });
};

Both queries are already in flight before either resolves - the round-trips overlap instead of queueing.

Timers in streams

Server-Sent Events handlers leans on timer for pacing - see WebSockets and SSE:

sub feed {
    my ($c, $stream) = @_;
    my $tick;
    $tick = sub {
        return unless $stream->is_open;
        $stream->send({ time => time });
        $c->timer(1)->on_done($tick);
    };
    $tick->();
}