NAME

Fetch::Future - a fast, native, Future-compatible async result

SYNOPSIS

my $f = Fetch::Future->new;
$f->on_done(sub { my $v = shift; ... });
$f->done(42);

my $all = Fetch::Future->needs_all($f1, $f2);
my @v   = $f->get;   # pumps the active loop (or Standalone) until ready

DESCRIPTION

An asynchronous result with an API compatible with CPAN Future: done/fail/cancel, on_ready/on_done/on_fail/on_cancel, then/else/followed_by/transform, get/await, and the convergent combinators wait_all/wait_any/needs_all/needs_any.

The implementation is entirely XS over an array-slot object; continuations are C closures trampolined through a fire queue, so long then-chains run iteratively with bounded stack depth.

get/await on a pending future pump the current event loop re-entrantly. Outside a running loop, an external loop may install $Fetch::Future::AWAIT (a coderef receiving the future) to make awaiting work.

as_cpan_future / from_future convert to and from CPAN Future objects for interop with isa('Future') code.

CONSTRUCTORS

new

my $f = Fetch::Future->new;

A new pending future.

done_future(@values) / fail_future(@failure)

my $f = Fetch::Future->done_future(42);
my $f = Fetch::Future->fail_future("nope\n");

An already-resolved future, done with @values or failed with @failure.

RESOLVING

done(@values)

Mark a pending future done with @values; fires its ready/done callbacks. Returns the future.

fail($message, @details)

Mark a pending future failed. Returns the future.

cancel

Cancel a pending future.

STATE

is_ready / is_done / is_failed / is_cancelled

Booleans: whether the future has settled at all, and how.

CALLBACKS

on_ready($cb)

Call $cb->($future) when the future settles (done, failed or cancelled). Returns the future.

on_done($cb) / on_fail($cb)

Call $cb->(@values) on success, or $cb->(@failure) on failure. Return the future.

on_cancel($cb) / on_cancel($future)

Given a coderef, call $cb->($future) when this future is cancelled; given another future, cancel that one with it. Registering on a future that has already been cancelled runs the target at once. Registering on one that completed normally drops it, because that future will never cancel. Returns the future.

CHAINING

Each returns a new future and flattens a future returned by the callback, so long chains compose without nesting.

then($on_done) / then($on_done, $on_fail)

Run $on_done with the values when this future succeeds; its result (a value list or a future) becomes the new future's result. Failure passes through unless $on_fail is given.

else($on_fail)

Like then but for the failure branch.

followed_by($cb)

Call $cb->($future) once this future settles either way, and adopt the future it returns.

transform(done => $cb, fail => $cb)

Map the done values and/or failure through the given coderefs.

AWAITING

get / result

my @values = $f->get;      # or a scalar in scalar context

Block until the future is ready, pumping the active event loop (the Standalone loop if none), then return its values - or rethrow its failure.

await

Block until ready (pumping the loop) and return the future itself.

failure

The failure of a failed future (the first failure value), or undef.

COMBINATORS

Each takes a list of futures and returns one future.

needs_all(@futures)

Done when all succeed (with their combined values); fails as soon as any fails.

needs_any(@futures)

Done as soon as any succeeds; fails only if all fail.

wait_all(@futures)

Done when all have settled, whatever the outcome.

wait_any(@futures)

Done as soon as any has settled.

INTEROP

as_cpan_future

A real CPAN Future that settles when this one does, for code expecting isa('Future').

from_future($cpan_future)

my $f = Fetch::Future->from_future($cpan_future);

A Fetch::Future that mirrors the given CPAN Future.

ASYNC/AWAIT

This class implements the Future::AsyncAwait::Awaitable API, so a request can be awaited directly:

use Future::AsyncAwait;

async sub fetch_title {
    my $res = await $ua->get($_[0]);
    return $res->content =~ m{<title>(.*?)</title>}s ? $1 : undef;
}

Whatever it awaited, an async sub returns a CPAN Future by default. Naming this class at import makes it return one of these instead:

use Future::AsyncAwait future_class => 'Fetch::Future';

Cancelling the future an async sub returned cancels whichever future it is suspended on.

An async sub takes its loop from the first request it awaits, so one that awaits requests made on two different loops cannot be awaited from outside either. This is the same single-loop rule a chain of then follows. The AWAIT_* methods are the protocol; call the documented names above rather than those.

AUTHOR

LNATION <email@lnation.org>

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION. This is free software, licensed under the Artistic License 2.0.