🚧 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 Future
s bloquées céderont le contrôle du processus,
permettant aux autres Future
s 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.