Dart 2. Programaci贸n asincr贸nica: futuros

Programaci贸n asincr贸nica: futuros


Contenido



Lo que es importante:


  • El c贸digo en Dart se ejecuta en una ejecuci贸n de subproceso 煤nico ( subproceso de nota - subproceso ).
  • Debido al c贸digo que toma (bloquea) el hilo durante mucho tiempo, el programa puede congelarse.
  • Future objetos futures ( futures ) representan los resultados de operaciones asincr贸nicas: procesamiento o E / S, que se completar谩n m谩s adelante.
  • Para suspender la ejecuci贸n hasta su finalizaci贸n en el futuro, use await en la funci贸n asincr贸nica (o then() cuando use la API Future ).
  • Para detectar errores, use la construcci贸n try-catch (o catchError() cuando use la API Future ) en la funci贸n asincr贸nica.
  • Para el procesamiento simult谩neo, cree un aislamiento (o trabajador para la aplicaci贸n web).

El c贸digo en Dart se ejecuta en un solo hilo de ejecuci贸n. Si el c贸digo est谩 ocupado con c谩lculos largos o est谩 esperando una operaci贸n de E / S, entonces todo el programa est谩 en pausa.


Las operaciones asincr贸nicas permiten que su programa complete otras tareas mientras espera que se complete la operaci贸n. Dart usa futures para presentar los resultados de operaciones asincr贸nicas. Tambi茅n puede usar async and await o Future API para trabajar con futures .


Una nota


Todo el c贸digo se ejecuta en el contexto del aislamiento, que posee toda la memoria utilizada por el c贸digo. No se puede iniciar m谩s de una ejecuci贸n de c贸digo en el mismo aislamiento.

Para la ejecuci贸n paralela de bloques de c贸digo, puede separarlos en aislamientos separados. (Las aplicaciones web utilizan trabajadores en lugar de aislamientos). Normalmente, cada uno de los aislamientos se ejecuta en su propio n煤cleo de procesador. Los aislamientos no comparten memoria, y la 煤nica forma en que pueden interactuar es envi谩ndose mensajes entre ellos. Para profundizar en el tema, consulte la documentaci贸n para aislamientos o trabajadores .

Introduccion


Veamos un ejemplo de c贸digo que puede "congelar" la ejecuci贸n del programa:


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

Nuestro programa lee las noticias del archivo del d铆a, las muestra y luego muestra informaci贸n que todav铆a es de inter茅s para el usuario:


 <gathered news goes here> Winning lotto numbers: [23, 63, 87, 26, 2] Tomorrow's forecast: 70F, sunny. Baseball score: Red Sox 10, Yankees 0 

En este ejemplo, el problema es que todas las operaciones despu茅s de llamar a gatherNewsReports() esperar谩n hasta que gatherNewsReports() devuelva el contenido del archivo, sin importar cu谩nto tiempo tarde. Si leer el archivo lleva mucho tiempo, el usuario se ver谩 obligado a esperar los resultados de la loter铆a, el pron贸stico del tiempo y el ganador de un juego reciente.


Para mantener la capacidad de respuesta de la aplicaci贸n, los autores de Dart utilizan un modelo asincr贸nico para identificar funciones que realizan un trabajo potencialmente costoso. Dichas funciones devuelven su valor utilizando futures .


驴Cu谩l es el futuro?


future es una instancia de la clase Future <T> , que es una operaci贸n asincr贸nica que devuelve un resultado de tipo T. Si no se utiliza el resultado de la operaci贸n, Future<void> indica el tipo de future . Cuando se llama a una funci贸n que devuelve el future , suceden dos cosas:


  1. La funci贸n se pone en cola para su ejecuci贸n y devuelve un objeto Future incompleto.
  2. M谩s tarde, cuando se completa la operaci贸n, el future termina con un valor o error.

Para escribir c贸digo dependiente del future , tiene dos opciones:


  • Usar async - await
  • Use Future API

As铆ncrono - espera


Las palabras clave async y en await son parte del soporte async de Dart. Le permiten escribir c贸digo asincr贸nico que se parece a un c贸digo s铆ncrono y no utiliza la API Future . Una funci贸n asincr贸nica es una funci贸n con la palabra clave async frente a su cuerpo. La palabra clave await solo funciona en funciones asincr贸nicas.


Nota: en Dart 1.x, las funciones asincr贸nicas retrasan inmediatamente la ejecuci贸n. En Dart 2, en lugar de pausar inmediatamente, las funciones asincr贸nicas se ejecutan sincr贸nicamente hasta que la primera await o return .

El siguiente c贸digo simula la lectura de noticias de un archivo usando async - await . Abra DartPad con la aplicaci贸n , inicie y haga clic en CONSOLA para ver el resultado.


C贸digo de ejemplo
 // 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', // ); 

Tenga en cuenta que primero llamamos a printDailyNewsDigest() , pero las noticias se imprimen en 煤ltimo lugar, incluso si el archivo contiene solo una l铆nea. Esto se debe a que el c贸digo que lee e imprime el archivo se ejecuta de forma asincr贸nica.


En este ejemplo, printDailyNewsDigest() hace una llamada a gatherNewsReports() , que no bloquea. Llamar al m茅todo gatherNewsReports() trabajo, pero no detiene la ejecuci贸n del resto del c贸digo. El programa muestra los n煤meros de loter铆a, el pron贸stico y la puntuaci贸n de un juego de b茅isbol; El programa imprime las noticias despu茅s de que se gatherNewsReports() la recopilaci贸n de gatherNewsReports() . Si gatherNewsReports() tarda un tiempo en completar su trabajo, no pasa nada malo: el usuario puede leer otras cosas antes de que se imprima el resumen de noticias diario.


Presta atenci贸n a los tipos de devoluci贸n. El tipo de retorno de la funci贸n gatherNewsReports() es Future<String> , lo que significa que devuelve un future que termina con un valor de cadena. La funci贸n printDailyNewsDigest() , que no devuelve un valor, tiene un tipo de retorno de Future<void> .


El siguiente diagrama muestra los pasos de ejecuci贸n del c贸digo.



  1. La aplicaci贸n comienza a ejecutarse.
  2. La funci贸n main() se printDailyNewsDigest() funci贸n asincr贸nica printDailyNewsDigest() , que comienza a ejecutarse sincr贸nicamente.
  3. printDailyNewsDigest() utiliza printDailyNewsDigest() para llamar a la funci贸n gatherNewsReports() , que comienza a ejecutarse.
  4. gatherNewsReports() devuelve un future inacabado (una instancia de Future<String> ).
  5. Dado que printDailyNewsDigest() es una funci贸n asincr贸nica y espera un valor, detiene la ejecuci贸n y devuelve el future incompleto (en este caso, Future<void> ) a la funci贸n main () llama.
  6. El resto de las funciones de salida se realizan. Como son sincr贸nicos, cada funci贸n se realiza por completo antes de pasar a la siguiente. Por ejemplo, todos los n煤meros de loter铆a ganadores se mostrar谩n antes del pron贸stico del tiempo.
  7. Despu茅s de completar main() las funciones asincr贸nicas pueden reanudar la ejecuci贸n. Primero obtenemos el future con noticias sobre la finalizaci贸n de gatherNewsReports() . Luego printDailyNewsDigest() contin煤a la ejecuci贸n, mostrando las noticias.
  8. Al final de la ejecuci贸n de printDailyNewsDigest() , future recibido originalmente y se cierra la aplicaci贸n.

Tenga en cuenta que la funci贸n asincr贸nica comienza inmediatamente (sincr贸nicamente). La funci贸n detiene la ejecuci贸n y devuelve un future inacabado cuando ocurre la primera ocurrencia de cualquiera de los siguientes:


  • La primera expresi贸n de await (despu茅s de que la funci贸n obtiene el future incompleto de esta expresi贸n).
  • Cualquier return en una funci贸n.
  • El final del cuerpo de la funci贸n.

Manejo de errores


Lo m谩s probable es que desee "detectar" un error en la ejecuci贸n de la funci贸n que devuelve el future . En funciones asincr贸nicas, puede manejar errores usando try-catch :


 Future<void> printDailyNewsDigest() async { try { var newsDigest = await gatherNewsReports(); print(newsDigest); } catch (e) { // Handle error... } } 

Un try-catch con c贸digo asincr贸nico se comporta igual que con el c贸digo s铆ncrono: si el c贸digo en el bloque try produce una excepci贸n, se ejecuta el c贸digo dentro de catch .


Ejecuci贸n secuencial


Puede usar m煤ltiples expresiones de await para asegurarse de que cada declaraci贸n se complete antes de ejecutar lo siguiente:


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

expensiveB() no se ejecuta hasta que se complete expensiveA() , y as铆 sucesivamente.


API futura


Antes de agregar async y await en Dart 1.9, ten铆a que usar la API Future . Todav铆a puede ver el uso de Future API en el c贸digo antiguo y en el c贸digo que necesita m谩s funcionalidades que async鈥揳wait tiene para ofrecer.


Para escribir c贸digo asincr贸nico usando la API Future , use el m茅todo then() para registrar la devoluci贸n de llamada. Esta devoluci贸n de llamada funcionar谩 cuando se complete el future .


El siguiente c贸digo simula la lectura de noticias de un archivo utilizando la API Future . Abra DartPad con la aplicaci贸n , inicie y haga clic en CONSOLA para ver el resultado.


C贸digo de ejemplo
 // 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', // ); 

Tenga en cuenta que primero llamamos a printDailyNewsDigest() , pero las noticias se imprimen en 煤ltimo lugar, incluso si el archivo contiene solo una l铆nea. Esto se debe a que el c贸digo que lee e imprime el archivo se ejecuta de forma asincr贸nica.


Esta aplicaci贸n se ejecuta de la siguiente manera:


  1. La aplicaci贸n comienza a ejecutarse.
  2. La funci贸n principal llama a printDailyNewsDigest() , que no devuelve el resultado inmediatamente, pero primero llama a gatherNewsReports() .
  3. gatherNewsReports() comienza a leer noticias y regresa en el future .
  4. printDailyNewsDigest() usa then() para registrar una devoluci贸n de llamada que tomar谩 como par谩metro el valor obtenido al final del future . La llamada then() devuelve un nuevo future , que termina con el valor devuelto por la devoluci贸n de llamada de then() .
  5. El resto de las funciones de salida se ejecutan. Como son sincr贸nicos, cada funci贸n se realiza por completo antes de pasar a la siguiente. Por ejemplo, todos los n煤meros de loter铆a ganadores se mostrar谩n antes del pron贸stico del tiempo.
  6. Cuando se han recibido todas las noticias, el future devuelto por la funci贸n gatherNewsReports() termina con una cadena que contiene las noticias recopiladas.
  7. El c贸digo especificado en then() en printDailyNewsDigest() se ejecuta para printDailyNewsDigest() noticias.
  8. La aplicaci贸n se est谩 cerrando.

Nota: en la funci贸n printDailyNewsDigest() , el c贸digo future.then(print) equivalente a lo siguiente: future.then((newsDigest) => print(newsDigest)) .

Adem谩s, el c贸digo dentro de then() puede usar llaves:


 Future<void> printDailyNewsDigest() { final future = gatherNewsReports(); return future.then((newsDigest) { print(newsDigest); // Do something else... }); } 

Debe especificar el argumento de devoluci贸n de llamada en then() , incluso si future es del tipo Future<void> . Por convenci贸n, un argumento no utilizado se define a trav茅s de _ (gui贸n bajo).


 final future = printDailyNewsDigest(); return future.then((_) { // Code that doesn't use the `_` parameter... print('All reports printed.'); }); 

Manejo de errores


Usando la API Future , puede detectar el error usando catchError() :


 Future<void> printDailyNewsDigest() => gatherNewsReports().then(print).catchError(handleError); 

Si las noticias no son legibles, el c贸digo anterior se ejecuta de la siguiente manera:


  1. future devuelto por gatherNewsReports() falla.
  2. future devuelto por then() falla, no se llama a print() .
  3. La catchError() de catchError() en catchError() ( handleError() ) handleError() el error, el future devuelto por catchError() completa normalmente y el error no se propaga m谩s.

La cadena catchError() - catchError() es un patr贸n com煤n cuando se usa la API Future . Considere este par como el equivalente de un try-catch en la API Future .

Al igual que then (), catchError () devuelve un nuevo future que termina con el valor de retorno de la devoluci贸n de llamada. Para profundizar en el tema, lea Futuros y manejo de errores .


Llamar a m煤ltiples funciones que devuelven el future


Consideremos tres funciones: expensiveA() , expensiveB() , expensiveC() , que devuelven el future . Puede llamarlos secuencialmente (una funci贸n comienza despu茅s de que se complete la anterior), o puede ejecutarlos todos al mismo tiempo y hacer algo tan pronto como regresen todos los valores. La interfaz de Future es lo suficientemente flexible como para implementar ambos casos de uso.


Una cadena de llamadas de funci贸n usando then()
Cuando las funciones que devuelven el future deben ejecutarse en orden, use la cadena desde then() :


 expensiveA() .then((aValue) => expensiveB()) .then((bValue) => expensiveC()) .then((cValue) => doSomethingWith(cValue)); 

Adjuntar devoluciones de llamada tambi茅n funciona, pero es m谩s dif铆cil de leer. ( nota http://callbackhell.com/ )


Esperando que se completen m煤ltiples futures usando Future.wait()
Si el orden de ejecuci贸n de las funciones no es importante, puede usar Future.wait() . Cuando especifica la lista de futures para los par谩metros de la funci贸n Future.wait (), inmediatamente devuelve el future . Este future no terminar谩 hasta que todos los futures especificados futures . Este future terminar谩 con una lista de los resultados de todos los futures indicados.


 Future.wait([expensiveA(), expensiveB(), expensiveC()]) .then((List responses) => chooseBestResponse(responses, moreInfo)) .catchError(handleError); 

Si falla una llamada a cualquiera de las funciones, tambi茅n falla el future devuelto por Future.wait() . Use catchError() para detectar este error.




驴Qu茅 m谩s leer?


Dart 2. Programaci贸n asincr贸nica: flujos de datos

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


All Articles