Menu

Reference / Async

Task

finalclass
final class Task

A running unit of async work: a green thread plus its settled state.

A Task is the handle returned by spawn() and TaskGroup::spawn(); it is never constructed directly. Every task is owned by exactly one scope (a TaskGroup) that cannot finish until the task has settled, so a task never outlives the code that started it.

Use await() to wait for the result (rethrowing the task's exception), join() to wait without rethrowing, awaitWithin() to wait with a deadline, and cancel() to ask it to stop. A failure nobody awaited is escalated to the owning scope, which cancels its other tasks and rethrows it when the scope ends. Unlike a Fiber, a task is scheduled for you: it starts running when the spawning task next suspends, and suspends transparently inside ordinary stream calls.

Its other public fields are scheduler bookkeeping, not API.

$t = spawn(fn() => compute());
$value = $t->await();

Example

use function Async\async;
use function Async\delay;
use function Async\spawn;

// async() runs the callable on the engine and returns its value.
$total = async(function () {
    // spawn() starts a task in the current scope and returns a handle.
    $slow = spawn(function () {
        delay(0.02);
        return 10;
    });
    $fast = spawn(function () {
        delay(0.01);
        return 32;
    });

    // await() suspends this task (not the process) until the other one settles.
    return $slow->await() + $fast->await();
});

echo "total: $total\n";

Output

total: 42

See also: spawn, TaskGroup, awaitAll

Methods

named()Label this task for diagnostics and return it, so it chains off spawn().
label()The task's identity as text: #id "label" at file:line.
describe()One diagnostic line for this task: its label, its state and what it is parked on.
isDone()Whether the task has settled, by returning, throwing or being cancelled.
cancel()Ask this task to stop.
await()Suspend the calling task until this one settles, then return its value or rethrow its error.
join()Wait for this task and report how it ended, without rethrowing.
awaitWithin()Await this task with a deadline.

named()#source

function named(string $name): Task

Label this task for diagnostics and return it, so it chains off spawn().

The label shows up in Task::label(), dump() and the watchdog and failure reports.

$t = spawn(fn() => serve())->named('http');

Parameters

$name string
the label

Returns

Task — this task

See also: dump, failure

label()#source

function label(): string

The task's identity as text: #id "label" at file:line.

The id is unique for the life of the process. The label appears only when set with named(). The location is where the task was spawned; a task started through $g->spawn(...) shows near file:line of the enclosing scope instead, and a task spawned from a dynamic callable may have none.

Returns

string — e.g. #3 "http" at server.php:31

describe()#source

function describe(): string

One diagnostic line for this task: its label, its state and what it is parked on.

The state is one of ready, done, failed(Class), timer, channel, select(...), io-read fd=N, io-write fd=N or offload op=..., followed by flags such as cancel-requested, shielded and awaited. This is the per-task line of dump().

Returns

string — the diagnostic line

See also: dump

isDone()#source

function isDone(): bool

Whether the task has settled, by returning, throwing or being cancelled.

Returns

bool — true once settled; false while still running or parked

cancel()#source

function cancel(): void

Ask this task to stop.

The task is taken off whatever it is waiting on (timer, channel, stream) and gets a CancelledException at that suspend point, and at every suspend point after it. cancel() returns immediately and does not wait: use join() to see the task out. Cancelling a task that has already settled does nothing; cancelling the running task only sets the flag, which it hits at its next suspend point. A task waiting on a blocking-pool file job cannot be stopped until that job finishes.

$t->cancel();
$t->join();     // wait for it to unwind, without rethrowing

Example

use Async\CancelledException;
use function Async\async;
use function Async\delay;
use function Async\shield;
use function Async\spawn;

async(function () {
    $worker = spawn(function () {
        try {
            delay(10.0);
        } catch (CancelledException $e) {
            // Cancellation is re-raised at every suspend point, so cleanup
            // that itself suspends must be shielded.
            shield(function () {
                delay(0.01);
                echo "cleanup finished\n";
            });
            throw $e;
        }
    });

    delay(0.01);
    $worker->cancel();

    // join() reports how the task ended without rethrowing it here.
    $outcome = $worker->join();
    echo $outcome->ok ? "completed\n" : "ended with " . get_class($outcome->error) . "\n";
});

Output

cleanup finished
ended with Async\CancelledException

See also: join, shield, CancelledException

await()#source

function await(): mixed

Suspend the calling task until this one settles, then return its value or rethrow its error.

Awaiting a task claims its outcome: a failure you await is not additionally escalated to the owning scope. If the awaited task was cancelled, its CancelledException is rethrown into the caller, which will unwind the caller's scope; use join() after cancelling a task yourself. The caller itself can be cancelled while it waits, in which case it is taken off the wait.

Returns

mixed — the task's return value

Throws

\Throwable
whatever the task threw

Example

use function Async\async;
use function Async\delay;
use function Async\spawn;

// async() runs the callable on the engine and returns its value.
$total = async(function () {
    // spawn() starts a task in the current scope and returns a handle.
    $slow = spawn(function () {
        delay(0.02);
        return 10;
    });
    $fast = spawn(function () {
        delay(0.01);
        return 32;
    });

    // await() suspends this task (not the process) until the other one settles.
    return $slow->await() + $fast->await();
});

echo "total: $total\n";

Output

total: 42

See also: join, awaitWithin, awaitAll

join()#source

function join(): Settled

Wait for this task and report how it ended, without rethrowing.

This is what you want after cancelling a task yourself: $t->cancel(); $t->join(); reads as "stop it and see it out", whereas await() would raise the task's own CancelledException into a caller that is not being cancelled.

Returns

Settled — the outcome: ok with value, or the error

Example

use Async\CancelledException;
use function Async\async;
use function Async\delay;
use function Async\shield;
use function Async\spawn;

async(function () {
    $worker = spawn(function () {
        try {
            delay(10.0);
        } catch (CancelledException $e) {
            // Cancellation is re-raised at every suspend point, so cleanup
            // that itself suspends must be shielded.
            shield(function () {
                delay(0.01);
                echo "cleanup finished\n";
            });
            throw $e;
        }
    });

    delay(0.01);
    $worker->cancel();

    // join() reports how the task ended without rethrowing it here.
    $outcome = $worker->join();
    echo $outcome->ok ? "completed\n" : "ended with " . get_class($outcome->error) . "\n";
});

Output

cleanup finished
ended with Async\CancelledException

See also: await, Settled

awaitWithin()#source

function awaitWithin(float $seconds): mixed

Await this task with a deadline.

On expiry the task is cancelled and joined (it does not keep running unobserved), and then TimeoutException is thrown. A timeout that leaves the work running is not a timeout.

Parameters

$seconds float
how long to wait, in seconds

Returns

mixed — the task's return value

Throws

TimeoutException
when the task did not settle in time
\Throwable
whatever the task threw

See also: timeout, await

View source