Dart 2. Programmation asynchrone: futurs

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:


 // Synchronous code void printDailyNewsDigest() { var newsDigest = gatherNewsReports(); // Can take a while. print(newsDigest); } main() { printDailyNewsDigest(); printWinningLotteryNumbers(); printWeatherForecast(); printBaseballScore(); } 

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:


  1. La fonction est en attente d'exécution et renvoie un objet Future incomplet.
  2. 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.


Exemple de code
 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:async'; Future<void> printDailyNewsDigest() async { var newsDigest = await gatherNewsReports(); print(newsDigest); } main() { printDailyNewsDigest(); printWinningLotteryNumbers(); printWeatherForecast(); printBaseballScore(); } printWinningLotteryNumbers() { print('Winning lotto numbers: [23, 63, 87, 26, 2]'); } printWeatherForecast() { print("Tomorrow's forecast: 70F, sunny."); } printBaseballScore() { print('Baseball score: Red Sox 10, Yankees 0'); } const news = '<gathered news goes here>'; const oneSecond = Duration(seconds: 1); // Imagine that this function is more complex and slow. :) Future<String> gatherNewsReports() => Future.delayed(oneSecond, () => news); // Alternatively, you can get news from a server using features // from either dart:io or dart:html. For example: // // import 'dart:html'; // // Future<String> gatherNewsReportsFromServer() => HttpRequest.getString( // 'https://www.dartlang.org/f/dailyNewsDigest.txt', // ); 

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.



  1. L'application démarre.
  2. La fonction main() est printDailyNewsDigest() fonction asynchrone printDailyNewsDigest() , qui commence à s'exécuter de manière synchrone.
  3. printDailyNewsDigest() utilise printDailyNewsDigest() pour appeler la fonction de gatherNewsReports() , qui commence à s'exécuter.
  4. gatherNewsReports() renvoie un future inachevé (une instance de Future<String> ).
  5. É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> ).
  6. 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.
  7. 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.
  8. À 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) { // Handle error... } } 

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:


 // Sequential processing using async and await. main() async { await expensiveA(); await expensiveB(); doSomethingWith(await expensiveC()); } 

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.


Exemple de code
 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:async'; Future<void> printDailyNewsDigest() { final future = gatherNewsReports(); return future.then(print); // You don't *have* to return the future here. // But if you don't, callers can't await it. } main() { printDailyNewsDigest(); printWinningLotteryNumbers(); printWeatherForecast(); printBaseballScore(); } printWinningLotteryNumbers() { print('Winning lotto numbers: [23, 63, 87, 26, 2]'); } printWeatherForecast() { print("Tomorrow's forecast: 70F, sunny."); } printBaseballScore() { print('Baseball score: Red Sox 10, Yankees 0'); } const news = '<gathered news goes here>'; const oneSecond = Duration(seconds: 1); // Imagine that this function is more complex and slow. :) Future<String> gatherNewsReports() => Future.delayed(oneSecond, () => news); // Alternatively, you can get news from a server using features // from either dart:io or dart:html. For example: // // import 'dart:html'; // // Future<String> gatherNewsReportsFromServer() => HttpRequest.getString( // 'https://www.dartlang.org/f/dailyNewsDigest.txt', // ); 

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:


  1. L'application démarre.
  2. La fonction principale appelle printDailyNewsDigest() , qui ne renvoie pas le résultat immédiatement, mais appelle d'abord la fonction gatherNewsReports() .
  3. gatherNewsReports() commence à lire les nouvelles et retourne dans le future .
  4. 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() .
  5. 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.
  6. 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.
  7. Le code spécifié dans then() dans printDailyNewsDigest() est exécuté pour printDailyNewsDigest() nouvelles.
  8. 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); // Do something else... }); } 

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((_) { // Code that doesn't use the `_` parameter... print('All reports printed.'); }); 

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:


  1. future renvoyé par gatherNewsReports() échoue.
  2. future retournée par then() échoue, print() pas appelé.
  3. 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

Source: https://habr.com/ru/post/fr442282/


All Articles