await
(PHP 8.6+, True Async 1.0)
await() — Attende il completamento di una coroutine, Async\Future o qualsiasi altro Async\Completable. Restituisce il risultato o lancia un'eccezione.
Descrizione
await(Async\Completable $awaitable, ?Async\Completable $cancellation = null): mixedSospende l'esecuzione della coroutine corrente fino al completamento dell'Async\Completable $awaitable specificato (o fino all'attivazione di $cancellation, se fornito) e restituisce il risultato. Se l'awaitable è già completato, il risultato viene restituito immediatamente.
Se la coroutine è terminata con un'eccezione, questa verrà propagata al codice chiamante.
Parametri
awaitable Un oggetto che implementa l'interfaccia Async\Completable (estende Async\Awaitable). Tipicamente questo è:
Async\Coroutine- il risultato della chiamata aspawn()Async\TaskGroup- un gruppo di taskAsync\Future- un valore futuro
cancellation Un oggetto opzionale Async\Completable; quando si completa, l'attesa verrà annullata.
Valori di ritorno
Restituisce il valore restituito dalla coroutine. Il tipo di ritorno dipende dalla coroutine.
Errori/Eccezioni
Se la coroutine è terminata con un'eccezione, await() rilancerà quella eccezione.
Se la coroutine è stata annullata, verrà lanciata Async\AsyncCancellation.
Se il token di cancellazione ($cancellation) si attiva, verrà lanciata Async\OperationCanceledException. L'eccezione originale del token è disponibile tramite $e->getPrevious(). Questo permette di distinguere l'attivazione del token da un'eccezione lanciata dall'oggetto awaitable stesso.
Come viene propagata l'eccezione
Quando una coroutine termina con un'eccezione, il risultato "si deposita" sul suo handle finché qualcuno non lo preleva. Il comportamento è simmetrico a Async\Future e dipende dal fatto che qualcuno trattenga l'handle della coroutine oltre allo Scheduler:
- L'handle è trattenuto (
$coro = spawn(...), la coroutine si trova in un array, è passata aawait_all()ecc.): l'eccezione resta sull'handle e attende. Qualunqueawait($coro)la riceverà, anche se la coroutine è terminata molto prima. - L'handle non è trattenuto da nessuno (fire-and-forget —
spawn(...)senza salvare il risultato): l'eccezione emerge alla distruzione dell'handle tramite la safety net fire-and-forget.
La conseguenza pratica fondamentale è che await cattura l'eccezione anche in caso di race:
use function Async\spawn;
use function Async\await;
$coro = spawn(function () {
throw new RuntimeException('boom');
});
// La coroutine può terminare prima che arriviamo ad await — è normale.
// L'eccezione ci attenderà tranquillamente qui:
try {
await($coro);
} catch (RuntimeException $e) {
echo "catturata: ", $e->getMessage(), "\n"; // catturata: boom
}Lo stesso vale per await_all(), await_any_or_fail() e le altre await_*(): puoi raccogliere le coroutine in un array, lasciarle lavorare in parallelo e poi attenderle. Le eccezioni vengono raccolte tramite await.
Quando il parent scope termina prima della propria coroutine, le coroutine figlie ricevono
AsyncCancellationper specifica. Quel ramo è gestito a parte e non dipende da chi trattiene l'handle.
Esempi
Esempio #1 Uso base di await()
<?php
use function Async\spawn;
use function Async\await;
$coroutine = spawn(function() {
return "Ciao, Async!";
});
echo await($coroutine); // Ciao, Async!
?>Esempio #2 Attesa sequenziale
<?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 "Utente: {$user['name']}\n";
echo "Post: " . count($posts) . "\n";
?>Esempio #3 Gestione delle eccezioni
<?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("Impossibile recuperare i dati");
}
return $response;
});
try {
$data = await($coroutine);
echo "Dati ricevuti\n";
} catch (RuntimeException $e) {
echo "Errore: " . $e->getMessage() . "\n";
}
?>Esempio #4 await con TaskGroup
<?php
use function Async\spawn;
use function Async\await;
use Async\TaskGroup;
$taskGroup = new TaskGroup();
$taskGroup->spawn(function() {
return "Risultato 1";
});
$taskGroup->spawn(function() {
return "Risultato 2";
});
$taskGroup->spawn(function() {
return "Risultato 3";
});
// Ottieni un array di tutti i risultati
$results = await($taskGroup);
print_r($results); // Array di risultati
?>Esempio #5 Await multipli sulla stessa coroutine
<?php
use function Async\spawn;
use function Async\await;
$coroutine = spawn(function() {
Async\timeout(1000);
return "Fatto";
});
// Il primo await attenderà il risultato
$result1 = await($coroutine);
echo "$result1\n";
// Gli await successivi restituiscono il risultato istantaneamente
$result2 = await($coroutine);
echo "$result2\n";
var_dump($result1 === $result2); // true
?>Esempio #6 await all'interno di una coroutine
<?php
use function Async\spawn;
use function Async\await;
spawn(function() {
echo "Coroutine genitore avviata\n";
$child = spawn(function() {
echo "Coroutine figlia in esecuzione\n";
Async\delay(1000);
return "Risultato dalla figlia";
});
echo "In attesa della figlia...\n";
$result = await($child);
echo "Ricevuto: $result\n";
});
echo "Il codice principale continua\n";
?>Changelog
| Versione | Descrizione |
|---|---|
| 1.0.0 | Aggiunta la funzione await() |