🚧 Attention, peinture fraîche !

Cette page a été traduite par une seule personne et n'a pas été relue et vérifiée par quelqu'un d'autre ! Les informations peuvent par exemple être erronées, être formulées maladroitement, ou contenir d'autres types de fautes.

Vous pouvez contribuer à l'amélioration de cette page sur sa Pull Request.

join!

La macro futures::join permet d'attendre que plusieurs futures différentes se terminent pendant qu'elles sont toutes exécutées en concurrence.

join!

Lorsque nous avons besoin de faire plusieurs opérations asynchrones, il peut être tentant d'utiliser .await en série sur elles :

async fn obtenir_livre_et_musique() -> (Livre, Musique) {
    let livre = obtenir_livre().await;
    let musique = obtenir_musique().await;
    (livre, musique)
}

En revanche, cela peut être plus lent que nécessaire, puisqu'il ne commence qu'à obtenir_musique avant que obtenir_livre soit terminé. Dans d'autres langages, les futures sont exécutées normalement jusqu'à leur fin, donc deux opérations peuvent être exécutées en concurrence en appelant chacune des async fn pour démarrer les futures, et ensuite attendre la fin des deux :

// MAUVAISE FAÇON -- ne faites pas cela
async fn obtenir_livre_et_musique() -> (Livre, Musique) {
    let future_livre = obtenir_livre();
    let future_musique = obtenir_musique();
    (future_livre.await, future_musique.await)
}

Malheureusement, les futures en Rust ne font rien tant qu'on n'utilise pas .await sur elles. Cela signifie que les deux extraits de code ci-dessus vont exécuter future_livre et future_musique en série au lieu de les exécuter en concurrence. Pour exécuter correctement les deux futures en concurrence, utilisons futures::join! :

use futures::join;

async fn obtenir_livre_et_musique() -> (Livre, Musique) {
    let future_livre = obtenir_livre();
    let future_musique = obtenir_musique();
    join!(future_livre, future_musique)
}

La valeur retournée par join! est une tuple contenant le résultat de chacune des Futures qu'on lui a donné.

try_join!

Pour les futures qui retournent Result, il vaut mieux utiliser try_join! plutôt que join!. Comme join! se termine uniquement lorsque toutes les sous-futures se soient terminées, il va continuer à calculer les autres futures même si une de ses sous-futures a retourné une Err.

Contrairement à join!, try_join! va se terminer tout de suite si une des sous-futures retourne une erreur.

use futures::try_join;

async fn obtenir_livre() -> Result<Livre, String> { /* ... */ Ok(Livre) }
async fn obtenir_musique() -> Result<Musique, String> { /* ... */ Ok(Musique) }

async fn obtenir_livre_et_musique() -> Result<(Livre, Musique), String> {
    let future_livre = obtenir_livre();
    let future_musique = obtenir_musique();
    try_join!(future_livre, future_musique)
}

Notez que les futures envoyées au try_join! doivent toutes avoir le même type d'erreur. Vous pouvez utiliser les fonctions .map_err(|e| ...) et .err_into() de futures::future::TryFutureExt pour regrouper les types d'erreurs :

use futures::{
    future::TryFutureExt,
    try_join,
};

async fn obtenir_livre() -> Result<Livre, ()> { /* ... */ Ok(Livre) }
async fn obtenir_musique() -> Result<Musique, String> { /* ... */ Ok(Musique) }

async fn obtenir_livre_et_musique() -> Result<(Livre, Musique), String> {
    let future_livre = obtenir_livre().map_err(|()| "Impossible d'obtenir le livre".to_string());
    let future_musique = obtenir_musique();
    try_join!(future_livre, future_musique)
}