Riferimento funzioniawait()

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 ​

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

Sospende 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 a spawn()
  • Async\TaskGroup - un gruppo di task
  • Async\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 a await_all() ecc.): l'eccezione resta sull'handle e attende. Qualunque await($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:

php
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 AsyncCancellation per specifica. Quel ramo è gestito a parte e non dipende da chi trattiene l'handle.

Esempi ​

Esempio #1 Uso base di await() ​

php
<?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
<?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
<?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
<?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
<?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
<?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 ​

VersioneDescrizione
1.0.0Aggiunta la funzione await()

Vedi anche ​