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
await(Async\Completable $awaitable, ?Async\Completable $cancellation = null): mixedSuspende 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 aspawn()Async\TaskGroup- un grupo de tareasAsync\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 aawait_all(), etc.): la excepción queda almacenada en el descriptor y espera. Cualquierawait($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:
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
AsyncCancellationsegú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
use function Async\spawn;
use function Async\await;
$coroutine = spawn(function() {
return "Hello, Async!";
});
echo await($coroutine); // Hello, Async!
?>Ejemplo #2 Espera secuencial
<?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
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
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
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
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ón | Descripción |
|---|---|
| 1.0.0 | Se añadió la función await() |