Programmation asynchrone: futurs
Table des matières
Ce qui est important:
- Le code dans Dart s'exécute dans une seule exécution de thread ( notez thread - thread ).
- En raison du code qui prend (bloque) le thread pendant une longue période, le programme peut se bloquer.
Future
objets Future
( futures
) représentent les résultats d'opérations asynchrones - traitement ou E / S, qui seront terminées plus tard.- Pour suspendre l'exécution jusqu'à la fin dans le futur, utilisez
await
dans la fonction asynchrone (ou then()
lorsque vous utilisez la Future
API). - Pour intercepter les erreurs, utilisez la construction
try-catch
(ou catchError()
lors de l'utilisation de l'API Future
) dans la fonction asynchrone. - Pour un traitement simultané, créez un isolat (ou un travailleur pour l'application Web).
Le code dans Dart s'exécute dans un seul thread d'exécution. Si le code est occupé par de longs calculs ou attend une opération d'E / S, l'ensemble du programme est suspendu.
Les opérations asynchrones permettent à votre programme d'effectuer d'autres tâches en attendant la fin de l'opération. Dart utilise des futures
pour présenter les résultats des opérations asynchrones. Vous pouvez également utiliser async et wait ou l'API Future pour travailler avec les futures
.
Une note
Tout le code est exécuté dans le contexte de l'isolat, qui possède toute la mémoire utilisée par le code. Plusieurs exécutions de code ne peuvent pas être démarrées dans le même isolat.
Pour l'exécution parallèle de blocs de code, vous pouvez les séparer en isolats distincts. (Les applications Web utilisent des travailleurs au lieu des isolats.) En général, chacun des isolats s'exécute sur son propre cœur de processeur. Les isolats ne partagent pas la mémoire et la seule façon d'interagir est de s'envoyer des messages. Pour plonger dans le sujet, consultez la documentation des isolats ou des travailleurs .
Présentation
Regardons un exemple de code qui peut «geler» l'exécution du programme:
Notre programme lit les nouvelles du fichier de la journée, les affiche, puis affiche les informations qui intéressent toujours l'utilisateur:
<gathered news goes here> Winning lotto numbers: [23, 63, 87, 26, 2] Tomorrow's forecast: 70F, sunny. Baseball score: Red Sox 10, Yankees 0
Dans cet exemple, le problème est que toutes les opérations après avoir appelé gatherNewsReports()
attendront jusqu'à gatherNewsReports()
que gatherNewsReports()
renvoie le contenu du fichier, quel que soit le temps qu'il prend. Si la lecture du fichier prend du temps, l'utilisateur sera obligé d'attendre les résultats de la loterie, les prévisions météo et le vainqueur d'une partie récente.
Pour maintenir la réactivité des applications, les auteurs de Dart utilisent un modèle asynchrone pour identifier les fonctions qui effectuent des travaux potentiellement coûteux. Ces fonctions renvoient leur valeur en utilisant des futures
à futures
.
Quel avenir?
future
est une instance de la classe Future <T> , qui est une opération asynchrone qui renvoie un résultat de type T. Si le résultat de l'opération n'est pas utilisé, le type de future
indiqué par Future<void>
. Lors de l'appel d'une fonction qui retourne future
, deux choses se produisent:
- La fonction est en attente d'exécution et renvoie un objet
Future
incomplet. - Plus tard, lorsque l'opération est terminée, le
future
termine avec une valeur ou une erreur.
Pour écrire du code dépendant du future
, vous avez deux options:
- Utiliser
async
- await
- Utiliser
Future
API
Async - attendre
Les mots clés async
et en await
font partie de la prise en charge async
de Dart. Ils vous permettent d'écrire du code asynchrone qui ressemble à du code synchrone et n'utilise pas l'API Future
. Une fonction asynchrone est une fonction avec le mot-clé async
devant son corps. Le mot-clé await
ne fonctionne que dans les fonctions asynchrones.
Remarque: dans Dart 1.x, les fonctions asynchrones retardent immédiatement l'exécution. Dans Dart 2, au lieu de s'arrêter immédiatement, les fonctions asynchrones s'exécutent de manière synchrone jusqu'à la première await
ou return
.
Le code suivant simule la lecture de nouvelles à partir d'un fichier en utilisant async
- await
. Ouvrez DartPad avec l'application , lancez et cliquez sur CONSOLE pour voir le résultat.
Notez que nous appelons d'abord printDailyNewsDigest()
, mais les nouvelles sont imprimées en dernier, même si le fichier ne contient qu'une seule ligne. Cela est dû au fait que le code qui lit et imprime le fichier s'exécute de manière asynchrone.
Dans cet exemple, printDailyNewsDigest()
effectue un appel à gatherNewsReports()
, qui n'est pas bloquant. L'appel de la méthode gatherNewsReports()
travail en gatherNewsReports()
, mais n'empêche pas le reste du code de s'exécuter. Le programme affiche les numéros de loterie, les prévisions et le score d'un match de baseball; Le programme imprime les nouvelles une fois la collecte de gatherNewsReports()
. Si gatherNewsReports()
prend un certain temps pour terminer son travail, rien de mal ne se produit: l'utilisateur peut lire d'autres choses avant l'impression du résumé des nouvelles quotidiennes.
Faites attention aux types de retour. Le type de retour de la fonction gatherNewsReports()
est Future<String>
, ce qui signifie qu'elle renvoie un future
qui se termine par une valeur de chaîne. La fonction printDailyNewsDigest()
, qui ne renvoie pas de valeur, a un type de retour Future<void>
.
Le diagramme suivant montre les étapes d'exécution du code.

- L'application démarre.
- La fonction
main()
est printDailyNewsDigest()
fonction asynchrone printDailyNewsDigest()
, qui commence à s'exécuter de manière synchrone. printDailyNewsDigest()
utilise printDailyNewsDigest()
pour appeler la fonction de gatherNewsReports()
, qui commence à s'exécuter.gatherNewsReports()
renvoie un future
inachevé (une instance de Future<String>
).- Étant donné que
printDailyNewsDigest()
est une fonction asynchrone et attend une valeur, elle suspend l'exécution et renvoie le future
inachevé future
main ()
(dans ce cas, une instance de Future<void>
). - Les autres fonctions de sortie sont exécutées. Comme ils sont synchrones, chaque fonction est exécutée complètement avant de passer à la suivante. Par exemple, tous les numéros de loterie gagnants seront affichés avant les prévisions météorologiques.
- Une fois la fonction
main()
terminée main()
les fonctions asynchrones peuvent reprendre leur exécution. Tout d'abord, nous obtenons l' future
avec des nouvelles sur l'achèvement de gatherNewsReports()
. Puis printDailyNewsDigest()
continue son exécution, affichant les nouvelles. - À la fin de l'exécution de
printDailyNewsDigest()
, l' future
initialement reçu future
et l'application se ferme.
Notez que la fonction asynchrone démarre immédiatement (de manière synchrone). La fonction suspend l'exécution et renvoie un future
inachevé lorsque la première occurrence de l'un des événements suivants se produit:
- La première expression en
await
(après que la fonction obtient l' future
incomplet de cette expression). - Toute
return
dans une fonction. - La fin du corps de fonction.
Gestion des erreurs
Vous aimeriez très probablement "attraper" une erreur dans l'exécution de la fonction qui retourne le future
. Dans les fonctions asynchrones, vous pouvez gérer les erreurs à l'aide de try-catch
:
Future<void> printDailyNewsDigest() async { try { var newsDigest = await gatherNewsReports(); print(newsDigest); } catch (e) {
Un try-catch
avec du code asynchrone se comporte de la même manière qu'avec le code synchrone: si le code du bloc try
une exception, le code à l'intérieur de catch
est exécuté.
Exécution séquentielle
Vous pouvez utiliser plusieurs expressions d' await
pour vous assurer que chaque instruction se termine avant d'exécuter les opérations suivantes:
expensiveB()
n'est pas exécutée avant la fin de expensiveA()
, etc.
Future API
Avant d'ajouter async
et async
dans Dart 1.9, vous deviez utiliser l'API Future
. Vous pouvez toujours voir l'utilisation de l'API Future
dans l'ancien code et dans le code qui a besoin de plus de fonctionnalités async–await
a à offrir.
Pour écrire du code asynchrone à l'aide de l'API Future
, utilisez la méthode then()
pour enregistrer le rappel. Ce rappel fonctionnera lorsque le future
terminé.
Le code suivant simule la lecture des actualités d'un fichier à l'aide de l'API Future
. Ouvrez DartPad avec l'application , lancez et cliquez sur CONSOLE pour voir le résultat.
Notez que nous appelons d'abord printDailyNewsDigest()
, mais les nouvelles sont imprimées en dernier, même si le fichier ne contient qu'une seule ligne. Cela est dû au fait que le code qui lit et imprime le fichier s'exécute de manière asynchrone.
Cette application s'exécute comme suit:
- L'application démarre.
- La fonction principale appelle
printDailyNewsDigest()
, qui ne renvoie pas le résultat immédiatement, mais appelle d'abord la fonction gatherNewsReports()
. gatherNewsReports()
commence à lire les nouvelles et retourne dans le future
.printDailyNewsDigest()
utilise then()
pour enregistrer un rappel qui prendra en paramètre la valeur obtenue à la fin du future
. L'appel then()
renvoie un nouvel future
, qui se termine par la valeur renvoyée par le rappel de then()
.- Les autres fonctions de sortie sont exécutées. Comme ils sont synchrones, chaque fonction est exécutée complètement avant de passer à la suivante. Par exemple, tous les numéros de loterie gagnants seront affichés avant les prévisions météorologiques.
- Lorsque toutes les nouvelles ont été reçues, l'
future
renvoyé par la fonction gatherNewsReports()
se termine par une chaîne contenant les nouvelles collectées. - Le code spécifié dans
then()
dans printDailyNewsDigest()
est exécuté pour printDailyNewsDigest()
nouvelles. - L'application se ferme.
Remarque: dans la fonction printDailyNewsDigest()
, le code future.then(print)
équivalent à ce qui suit: future.then((newsDigest) => print(newsDigest))
.
De plus, le code à l'intérieur de then()
peut utiliser des accolades:
Future<void> printDailyNewsDigest() { final future = gatherNewsReports(); return future.then((newsDigest) { print(newsDigest);
Vous devez spécifier l'argument de rappel dans then()
, même si future
est de type Future<void>
. Par convention, un argument inutilisé est défini via _
(trait de soulignement).
final future = printDailyNewsDigest(); return future.then((_) {
Gestion des erreurs
En utilisant l'API Future
, vous pouvez intercepter l'erreur en utilisant catchError()
:
Future<void> printDailyNewsDigest() => gatherNewsReports().then(print).catchError(handleError);
Si les nouvelles ne sont pas lisibles, le code ci-dessus est exécuté comme suit:
future
renvoyé par gatherNewsReports()
échoue.future
retournée par then()
échoue, print()
pas appelé.- Le
catchError()
dans catchError()
( handleError()
) handleError()
l'erreur, l' future
renvoyé par catchError()
termine normalement et l'erreur ne se propage pas plus loin.
La chaîne catchError()
- catchError()
est un modèle courant lors de l'utilisation de l'API Future
. Considérez cette paire comme l'équivalent d'un try-catch
dans l'API Future
.
Comme then (), catchError () retourne un nouvel future
qui se termine par la valeur de retour du rappel. Pour plonger dans le sujet, lisez Futures and Error Handling .
Appeler plusieurs fonctions pour retourner le future
Considérons trois fonctions: expensiveA()
, expensiveB()
, expensiveC()
, qui renvoient le future
. Vous pouvez les appeler séquentiellement (une fonction démarre une fois la précédente terminée), ou vous pouvez toutes les exécuter en même temps et faire quelque chose dès que toutes les valeurs reviennent. L'interface de Future est suffisamment flexible pour implémenter les deux cas d'utilisation.
Une chaîne d'appels de fonction utilisant then()
Lorsque les fonctions renvoyant le future
doivent être exécutées dans l'ordre, utilisez la chaîne de then()
:
expensiveA() .then((aValue) => expensiveB()) .then((bValue) => expensiveC()) .then((cValue) => doSomethingWith(cValue));
Joindre des rappels fonctionne également, mais c'est plus difficile à lire. ( notez http://callbackhell.com/ )
Attendre la fin de plusieurs futures
en utilisant Future.wait()
Si l'ordre d'exécution des fonctions n'est pas important, vous pouvez utiliser Future.wait()
. Lorsque vous spécifiez la liste des futures
pour les paramètres de la fonction Future.wait (), elle renvoie immédiatement le future
. Cet future
ne prendra fin que lorsque tous les futures
spécifiés futures
. Cet future
se terminera par une liste des résultats de tous les futures
indiqués.
Future.wait([expensiveA(), expensiveB(), expensiveC()]) .then((List responses) => chooseBestResponse(responses, moreInfo)) .catchError(handleError);
Si un appel à l'une des fonctions échoue, le future
renvoyé par Future.wait()
échoue également. Utilisez catchError()
pour intercepter cette erreur.
Que lire d'autre?
Dart 2. Programmation asynchrone: flux de données