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->();
}