🚧 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.

Introduction Ă  async et await

Le async et await sont les outils intégrés dans Rust pour écrire des fonctions asynchrones qui ressemblent à du code synchrone. async transforme un bloc de code en une machine à états qui implémente le trait Future. Alors que l'appel à une fonction bloquante dans une méthode synchrone va bloquer tout le processus, les Futures bloquées céderont le contrÎle du processus, permettant aux autres Futures de s'exécuter.

Ajoutons quelques dépendances au fichier Cargo.toml :

[dependencies]
futures = "0.3"

Pour créer une fonction asynchrone, vous pouvez utiliser la syntaxe async fn :


#![allow(unused)]
fn main() {
async fn faire_quelquechose() { /* ... */ }
}

La valeur retournĂ©e par async fn est une Future. Pour que quelque chose se produise, la Future a besoin d'ĂȘtre exĂ©cutĂ©e avec un exĂ©cuteur.

// `block_on` bloque le processus en cours jusqu'Ă  ce que la future qu'on lui
// donne ait terminé son exécution. Les autres exécuteurs ont un comportement
// plus complexe, comme par exemple ordonnancer plusieurs futures sur le mĂȘme
// processus.
use futures::executor::block_on;

async fn salutations() {
    println!("salutations !");
}

fn main() {
    let future = salutations(); // rien n'est pas affiché
    block_on(future); // `future` est exécuté et "salutations !" est affiché
}

Dans une async fn, vous pouvez utiliser .await pour attendre la fin d'un autre type qui implémente le trait Future, comme le résultat d'une autre async fn. Contrairement à block_on, .await ne bloque pas le processus en cours, mais attends plutÎt de maniÚre asynchrone que la future se termine, pour permettre aux autres tùches de s'exécuter si cette future n'est pas en mesure de progresser actuellement.

Par exemple, imaginons que nous ayons trois async fn : apprendre_chanson, chanter_chanson, et danser :

async fn apprendre_chanson() -> Chanson { /* ... */ }
async fn chanter_chanson(chanson: Chanson) { /* ... */ }
async fn danser() { /* ... */ }

Une façon d'apprendre, chanter, et danser serait de bloquer sur chacun :

fn main() {
    let chanson = block_on(apprendre_chanson());
    block_on(chanter_chanson(chanson));
    block_on(danser());
}

Cependant, nous ne profitons pas de performances optimales de cette maniĂšre — nous ne faisons qu'une seule chose Ă  fois ! Il faut que nous apprenions la chanson avant de pouvoir la chanter, mais il reste possible de danser en mĂȘme temps qu'on apprends et qu'on chante la chanson. Pour pouvoir faire cela, nous pouvons crĂ©er deux async fn qui peuvent ĂȘtre exĂ©cutĂ©s en concurrence :

async fn apprendre_et_chanter() {
    // Attends (await) que la chanson soit apprise avant de la chanter.
    // Nous utilisons ici `.await` plutĂŽt que `block_on` pour Ă©viter de bloquer
    // le processus, ce qui rend possible de `danser` en mĂȘme temps.
    let chanson = apprendre_chanson().await;
    chanter_chanson(chanson).await;
}

async fn async_main() {
    let f1 = apprendre_et_chanter();
    let f2 = danser();

    // `join!` se comporte comme `.await`, mais permet d'attendre plusieurs
    // futures en concurrence. Si nous avions bloqué temporairement dans la
    // future `apprendre_et_chanter`, la future `danser` aurais pris le relais
    // dans le processus d'exécution en cours. Si `danser` se bloque aussi,
    // `apprendre_et_chanter` pourra continuer dans le processus en cours. Si
    // les deux futures sont bloquées, et bien `async_main` est bloqué et va en
    // informer son exécuteur.
    futures::join!(f1, f2);
}

fn main() {
    block_on(async_main());
}

Dans cet exemple, la chanson doit ĂȘtre apprise avant de chanter la chanson, mais l'apprentissage et le chant peuvent se dĂ©rouler en mĂȘme temps qu'on danse. Si nous avions utilisĂ© block_on(apprendre_chanson()) plutĂŽt que apprendre_chanson().await dans apprendre_et_chanter, le processus n'aurait rien pu faire tant que apprendre_chanson s'exĂ©cutait. Cela aurait rendu impossible de pouvoir danser en mĂȘme temps. En attendant la future apprendre_chanson, grĂące Ă  await, nous permettons aux autres tĂąches de prendre le relais dans le processus en cours d'exĂ©cution lorsque apprendre_chanson est bloquĂ©. Cela permet d'exĂ©cuter plusieurs futures jusqu'Ă  leur fin de maniĂšre concurrente au sein du mĂȘme processus.