await

(PHP 8.6+, True Async 1.0)

await() — Espera a que una corrutina, Async\Future, o cualquier otro Async\Completable se complete. Devuelve el resultado o lanza una excepción.

Descripción

php
await(Async\Completable $awaitable, ?Async\Completable $cancellation = null): mixed

Suspende la ejecución de la corrutina actual hasta que el Async\Completable $awaitable especificado se complete (o hasta que $cancellation se active, si se proporciona) y devuelve el resultado. Si el awaitable ya se ha completado, el resultado se devuelve inmediatamente.

Si la corrutina terminó con una excepción, esta se propagará al código que la invocó.

Parámetros

awaitable Un objeto que implementa la interfaz Async\Completable (extiende Async\Awaitable). Típicamente es:

  • Async\Coroutine - el resultado de llamar a spawn()
  • Async\TaskGroup - un grupo de tareas
  • Async\Future - un valor futuro

cancellation Un objeto Async\Completable opcional; cuando se completa, la espera será cancelada.

Valores de retorno

Devuelve el valor que retornó la corrutina. El tipo de retorno depende de la corrutina.

Errores/Excepciones

Si la corrutina terminó con una excepción, await() relanzará esa excepción.

Si la corrutina fue cancelada, se lanzará Async\AsyncCancellation.

Si se activa el token de cancelación ($cancellation), se lanzará Async\OperationCanceledException. La excepción original del token está disponible a través de $e->getPrevious(). Esto permite distinguir la activación del token de una excepción lanzada por el propio objeto awaitable.

Cómo se transmite la excepción

Cuando una corrutina termina con una excepción, el resultado "se asienta" en su descriptor hasta que alguien lo recoge. El comportamiento es simétrico al de Async\Future y depende de si alguien retiene el descriptor de la corrutina además del Scheduler:

  • Hay un descriptor retenido ($coro = spawn(...), la corrutina guardada en un array, pasada a await_all(), etc.): la excepción queda almacenada en el descriptor y espera. Cualquier await($coro) la recibirá, incluso si la corrutina terminó hace tiempo.
  • El descriptor no lo retiene nadie (fire-and-forget — spawn(...) sin guardar el resultado): la excepción se manifiesta al destruir el handle a través de la safety net de fire-and-forget.

La consecuencia práctica más importante es que await captura la excepción incluso ante una carrera:

php
use function Async\spawn;
use function Async\await;

$coro = spawn(function () {
    throw new RuntimeException('boom');
});

// La corrutina puede terminar antes de que lleguemos al await: es normal.
// La excepción esperará tranquilamente aquí:
try {
    await($coro);
} catch (RuntimeException $e) {
    echo "capturada: ", $e->getMessage(), "\n"; // capturada: boom
}

Lo mismo aplica a await_all(), await_any_or_fail() y al resto de await_*(): puedes reunir corrutinas en un array, dejar que trabajen concurrentemente y luego esperarlas. Las excepciones se recogen mediante await.

Cuando el parent-scope muere antes que su corrutina, las corrutinas hijas reciben AsyncCancellation según la especificación. Esa rama se trata aparte y no depende de quién retenga el descriptor.

Ejemplos

Ejemplo #1 Uso básico de await()

php
<?php
use function Async\spawn;
use function Async\await;

$coroutine = spawn(function() {
    return "Hello, Async!";
});

echo await($coroutine); // Hello, Async!
?>

Ejemplo #2 Espera secuencial

php
<?php
use function Async\spawn;
use function Async\await;

function fetchUser(int $id): array {
    return json_decode(
        file_get_contents("https://api/users/$id"),
        true
    );
}

function fetchPosts(int $userId): array {
    return json_decode(
        file_get_contents("https://api/posts?user=$userId"),
        true
    );
}

$userCoro = spawn(fetchUser(...), 123);
$user = await($userCoro);

$postsCoro = spawn(fetchPosts(...), $user['id']);
$posts = await($postsCoro);

echo "User: {$user['name']}\n";
echo "Posts: " . count($posts) . "\n";
?>

Ejemplo #3 Manejo de excepciones

php
<?php
use function Async\spawn;
use function Async\await;

$coroutine = spawn(function() {
    $response = file_get_contents('https://api.com/data');

    if ($response === false) {
        throw new RuntimeException("Error al obtener datos");
    }

    return $response;
});

try {
    $data = await($coroutine);
    echo "Datos recibidos\n";
} catch (RuntimeException $e) {
    echo "Error: " . $e->getMessage() . "\n";
}
?>

Ejemplo #4 await con TaskGroup

php
<?php
use function Async\spawn;
use function Async\await;
use Async\TaskGroup;

$taskGroup = new TaskGroup();

$taskGroup->spawn(function() {
    return "Result 1";
});

$taskGroup->spawn(function() {
    return "Result 2";
});

$taskGroup->spawn(function() {
    return "Result 3";
});

// Obtener un array con todos los resultados
$results = await($taskGroup);
print_r($results); // Array de resultados
?>

Ejemplo #5 Múltiples await sobre la misma corrutina

php
<?php
use function Async\spawn;
use function Async\await;

$coroutine = spawn(function() {
    Async\timeout(1000);
    return "Done";
});

// El primer await esperará el resultado
$result1 = await($coroutine);
echo "$result1\n";

// Los awaits posteriores devuelven el resultado instantáneamente
$result2 = await($coroutine);
echo "$result2\n";

var_dump($result1 === $result2); // true
?>

Ejemplo #6 await dentro de una corrutina

php
<?php
use function Async\spawn;
use function Async\await;

spawn(function() {
    echo "Corrutina padre iniciada\n";

    $child = spawn(function() {
        echo "Corrutina hija ejecutándose\n";
        Async\delay(1000);
        return "Resultado de la hija";
    });

    echo "Esperando a la hija...\n";
    $result = await($child);
    echo "Recibido: $result\n";
});

echo "El código principal continúa\n";
?>

Registro de cambios

VersiónDescripción
1.0.0Se añadió la función await()

Ver también