stringtranslate.com

asíncrono/espera

En programación de computadoras , el patrón async/await es una característica sintáctica de muchos lenguajes de programación que permite estructurar una función asincrónica y sin bloqueo de una manera similar a una función síncrona ordinaria. Está semánticamente relacionado con el concepto de corrutina y a menudo se implementa utilizando técnicas similares, y su objetivo principal es brindar oportunidades para que el programa ejecute otro código mientras espera que se complete una tarea asincrónica de larga duración, generalmente representada por promesas o estructuras de datos similares . La característica se encuentra en C# , [1] : 10  C++ , Python , F# , Hack , Julia , Dart , Kotlin , Rust , [2] Nim , [3] JavaScript , Swift [4] y Zig . [5]

Historia

F# agregó flujos de trabajo asíncronos con puntos de espera en la versión 2.0 en 2007. [6] Esto influyó en el mecanismo asíncrono/en espera agregado a C#. [7]

Microsoft lanzó por primera vez una versión de C# con async/await en Async CTP (2011). Posteriormente se lanzó oficialmente en C# 5 (2012). [8] [1] : 10 

El desarrollador principal de Haskell, Simon Marlow, creó el paquete asíncrono en 2012. [9]

Python agregó soporte para async/await con la versión 3.5 en 2015 [10] agregando 2 nuevas palabrasasync clave y await.

TypeScript agregó soporte para async/await con la versión 1.7 en 2015. [11]

JavaScript agregó soporte para async/await en 2017 como parte de la edición JavaScript ECMAScript 2017.

Rust agregó soporte para async/await con la versión 1.39.0 en 2019 usando la asyncpalabra clave y el .awaitoperador postfix, ambos introducidos en la edición 2018 del lenguaje. [12]

C++ agregó soporte para async/await con la versión 20 en 2020 con 3 nuevas palabras clave co_return,, co_await.co_yield

Swift agregó soporte para async/await con la versión 5.5 en 2021, agregando 2 nuevas palabras clave asyncy await. Esto se publicó junto con una implementación concreta del modelo Actor con la actorpalabra clave [13] que utiliza async/await para mediar el acceso a cada actor desde el exterior.

Ejemplo C#

La siguiente función de C# , que descarga un recurso desde un URI y devuelve la longitud del recurso, utiliza este patrón async/await:

Tarea asíncrona pública <int> FindPageSizeAsync ( Uri uri ) { var client = new HttpClient ( ) ; byte [] datos = espera cliente . GetByteArrayAsync ( uri ); datos de retorno . Longitud ; }                 

Una función que usa async/await puede usar tantas awaitexpresiones como quiera, y cada una se manejará de la misma manera (aunque solo se devolverá una promesa a la persona que llama para la primera espera, mientras que todas las demás esperas utilizarán devoluciones de llamada internas). Una función también puede contener un objeto de promesa directamente y realizar otros procesos primero (incluido el inicio de otras tareas asincrónicas), retrasando la espera de la promesa hasta que se necesite su resultado. Las funciones con promesas también tienen métodos de agregación de promesas que permiten al programa esperar múltiples promesas a la vez o en algún patrón especial (como C# Task.WhenAll(), [1] : 174–175  [14] : 664–665  que devuelve un valor sin valor Taskque se resuelve cuando todas de las tareas en los argumentos han sido resueltas). Muchos tipos de promesas también tienen características adicionales más allá de las que normalmente usa el patrón async/await, como poder configurar más de una devolución de llamada de resultados o inspeccionar el progreso de una tarea de ejecución especialmente larga.

En el caso particular de C#, y en muchos otros lenguajes con esta característica de lenguaje, el patrón async/await no es una parte central del tiempo de ejecución del lenguaje, sino que se implementa con lambdas o continuaciones en tiempo de compilación. Por ejemplo, el compilador de C# probablemente traduciría el código anterior a algo como lo siguiente antes de traducirlo a su formato de código de bytes IL:

Tarea pública <int> FindPageSizeAsync ( Uri uri ) { var cliente = nuevo HttpClient ( ) ; Tarea < byte [] > dataTask = cliente . GetByteArrayAsync ( uri ); Tarea <int> afterDataTask = dataTask .Continuar con (( tarea original ) => { retornar tarea original . Resultado . Longitud ; }); devolver después de la tarea de datos ; }                        

Debido a esto, si un método de interfaz necesita devolver un objeto de promesa, pero no requiere que awaitel cuerpo espere ninguna tarea asincrónica, tampoco necesita el asyncmodificador y, en su lugar, puede devolver un objeto de promesa directamente. Por ejemplo, una función podría proporcionar una promesa que se resuelve inmediatamente en algún valor de resultado (como C# Task.FromResult()[14] : 656  ), o simplemente puede devolver la promesa de otro método que resulta ser la promesa exacta necesaria (como cuando aplazar una sobrecarga).

Sin embargo, una advertencia importante de esta funcionalidad es que, si bien el código se parece al código de bloqueo tradicional, en realidad no es de bloqueo y es potencialmente multiproceso, lo que significa que pueden ocurrir muchos eventos intermedios mientras se espera que se awaitresuelva la promesa dirigida por un. Por ejemplo, el siguiente código, si bien siempre tiene éxito en un modelo de bloqueo sin await, puede experimentar eventos intermedios durante el awaity, por lo tanto, puede encontrar que el estado compartido ha cambiado desde debajo de él:

var a = estado . a ; cliente var = nuevo HttpClient (); var datos = esperar cliente . GetByteArrayAsync ( uri ); Depurar . Afirmar ( a == estado . a ); // Posible error, ya que el valor de state.a puede haber sido cambiado // por el controlador del evento potencialmente interventor. datos de retorno . Longitud ;                

Implementaciones

En fa#

En 2007, F# agregó flujos de trabajo asincrónicos con la versión 2.0. [15] Los flujos de trabajo asincrónicos se implementan como CE ( expresiones de cálculo ). Se pueden definir sin especificar ningún contexto especial (como asyncen C#). Los flujos de trabajo asincrónicos de F# añaden un bang (!) a las palabras clave para iniciar tareas asincrónicas.

La siguiente función asíncrona descarga datos de una URL mediante un flujo de trabajo asíncrono:

let asyncSumPageSizes ( uris : # seq < Uri > ) : Async < int > = async { use httpClient = new HttpClient () let! páginas = uris |> Sec . mapa ( httpClient . GetStringAsync >> Async . AwaitTask ) |> Async . Páginas de retorno paralelas |> Sec . plegar ( acumulador divertido actual -> actual . Longitud + acumulador ) 0 }                                    

Cª#

En 2012, C# agregó el patrón async/await en C# con la versión 5.0, al que Microsoft se refiere como patrón asíncrono basado en tareas (TAP). [16] Los métodos asíncronos generalmente devuelven void, Task, Task<T>, [14] : 35  [17] : 546–547  [1] : 22, 182  ValueTask o ValueTask<T>. [14] : 651–652  [1] : 182–184  El código de usuario puede definir tipos personalizados que los métodos asíncronos pueden devolver a través de constructores de métodos asíncronos personalizados , pero este es un escenario avanzado y poco común. [18] Los métodos asíncronos que regresan voidestán destinados a controladores de eventos ; en la mayoría de los casos en los que un método sincrónico devolvería void, Taskse recomienda devolver, ya que permite un manejo de excepciones más intuitivo. [19]

Los métodos que hacen uso awaitdeben declararse con la asyncpalabra clave. En los métodos que tienen un valor de retorno de tipo Task<T>, los métodos declarados con asyncdeben tener una declaración de retorno de tipo asignable a Ten lugar de Task<T>; el compilador envuelve el valor en el Task<T>genérico. También es posible utilizar awaitmétodos que tengan un tipo de retorno de Tasko Task<T>que se declaren sin él async.

El siguiente método asíncrono descarga datos de una URL usando await. Debido a que este método emite una tarea para cada uri antes de requerir su finalización con la awaitpalabra clave, los recursos se pueden cargar al mismo tiempo en lugar de esperar a que finalice el último recurso antes de comenzar a cargar el siguiente.

tarea asíncrona pública < int > SumPageSizesAsync ( IEnumerable < Uri > uris ) { var client = new HttpClient (); entero total = 0 ; var loadUriTasks = nueva Lista < Tarea < byte [] >> ();                    foreach ( var uri en uris ) { var loadUriTask = cliente . GetByteArrayAsync ( uri ); cargarUriTasks . Agregar ( loadUriTask ); }             foreach ( var loadUriTask en loadUriTasks ) { statusText . Texto = $"Encontrados {total} bytes..." ; var recursoAsBytes = espera loadUriTask ; total += recursoAsBytes . Longitud ; }                  texto de estado . Texto = $"Encontrado {total} bytes en total" ;   total de retorno ; } 

En pitón

Python 3.5 (2015) [20] ha agregado soporte para async/await como se describe en PEP 492 (escrito e implementado por Yury Selivanov). [21]

importar  asincioasync  def  main ():  print ( "hola" )  espera  asyncio . dormir ( 1 )  imprimir ( "mundo" )asincio . ejecutar ( principal ())

En JavaScript

El operador de espera en JavaScript sólo se puede utilizar desde dentro de una función asíncrona o en el nivel superior de un módulo . Si el parámetro es una promesa , la ejecución de la función asíncrona se reanudará cuando se resuelva la promesa (a menos que se rechace la promesa, en cuyo caso se generará un error que se puede manejar con el manejo normal de excepciones de JavaScript ). Si el parámetro no es una promesa, el parámetro en sí se devolverá inmediatamente. [22]

Muchas bibliotecas proporcionan objetos de promesa que también se pueden usar con await, siempre que coincidan con la especificación de las promesas nativas de JavaScript. Sin embargo, las promesas de la biblioteca jQuery no eran compatibles con Promises/A+ hasta jQuery 3.0. [23]

Aquí hay un ejemplo (modificado de este artículo [24] ):

función asíncrona createNewDoc () { dejar respuesta = esperar db . correo ({}); // publicar un nuevo documento devolver db . obtener ( respuesta . id ); // buscar por id }            función asíncrona principal () { intentar { dejar doc = esperar crearNuevoDoc (); consola . iniciar sesión ( doc ); } captura ( err ) { consola . iniciar sesión ( errar ); } } principal ();                 

Node.js versión 8 incluye una utilidad que permite utilizar los métodos basados ​​en devolución de llamada de la biblioteca estándar como promesas. [25]

En C++

En C++, await (llamado co_await en C++) se ha fusionado oficialmente en la versión 20 . [26] El soporte para él, las corrutinas y las palabras clave como co_awaitestán disponibles en los compiladores GCC y MSVC, mientras que Clang tiene soporte parcial.

Vale la pena señalar que std::promise y std::future, aunque parecería que serían objetos en espera, no implementan ninguna de las maquinarias necesarias para ser devueltos por las corrutinas y ser esperados usando co_await. Los programadores deben implementar una serie de funciones de miembros públicos, como await_ready, await_suspendy await_resumeen el tipo de retorno para que se espere el tipo. Los detalles se pueden encontrar en cppreference. [27]

#include <iostream> #include "CustomAwaitableTask.h"  usando el espacio de nombres estándar ;  CustomAwaitableTask < int > agregar ( int a , int b ) { int c = a + b ; co_retorno c ; }            CustomAwaitableTask < int > prueba () { int ret = co_await add ( 1 , 2 ); cout << "retorno" << ret << endl ; co_return ret ; }                int main () { tarea automática = prueba ();      devolver 0 ; } 

El lenguaje C no admite await/async. Algunas bibliotecas de rutinas como s_task [28] simulan las palabras clave await/async con macros.

#incluye <stdio.h> #incluye "s_task.h"  // define la memoria de pila para tareas int g_stack_main [ 64 * 1024 / sizeof ( int )]; int g_stack0 [ 64 * 1024 / tamaño de ( int )]; int g_stack1 [ 64 * 1024 / tamaño de ( int )];               void sub_task ( __async__ , void * arg ) { int i ; int n = ( int ) ( tamaño_t ) arg ; for ( i = 0 ; i < 5 ; ++ i ) { printf ( "tarea %d, segundos de retraso = %d, i = %d \n " , n , n , i ); s_task_msleep ( __await__ , n * 1000 ); //s_task_yield(__await__); } }                             void main_task ( __async__ , void * arg ) { int i ;       // crea dos subtareas s_task_create ( g_stack0 , sizeof ( g_stack0 ), sub_task , ( void * ) 1 ); s_task_create ( g_stack1 , tamaño de ( g_stack1 ), sub_tarea , ( void * ) 2 );         for ( i = 0 ; i < 4 ; ++ i ) { printf ( "task_main arg = %p, i = %d \n " , arg , i ); s_task_yield ( __await__ ); }              // espera a que salgan las subtareas s_task_join ( __await__ , g_stack0 ); s_task_join ( __await__ , g_stack1 ); }    int principal ( int argc , char * argv ) {      s_task_init_system (); //crea la tarea principal s_task_create ( g_stack_main , sizeof ( g_stack_main ), main_task , ( void * )( size_t ) argc ); s_task_join ( __await__ , g_stack_main ); printf ( "toda la tarea ha terminado \n " ); devolver 0 ; }         


En Perl 5

El módulo Future::AsyncAwait [29] fue objeto de una subvención de la Fundación Perl en septiembre de 2018. [30]

En óxido

El 7 de noviembre de 2019, se lanzó async/await en la versión estable de Rust. [31] Las funciones asíncronas en Rust se convierten en funciones simples que devuelven valores que implementan el rasgo Futuro. Actualmente se implementan con una máquina de estados finitos . [32]

// En el Cargo.toml de la caja, necesitamos `futures = "0.3.0"` en la sección de dependencias, // para que podamos usar la caja de futurosfuturos de cajas externas ; // Actualmente no hay ningún ejecutor en la biblioteca `std`.   // Esto se desazucará a algo como // `fn async_add_one(num: u32) -> impl Future<Output = u32>` async fn async_add_one ( num : u32 ) -> u32 { num + 1 }       async fn example_task () { let número = async_add_one ( 5 ). esperar ; imprimir! ( "5 + 1 = {}" , número ); }         fn  main () { // Crear el futuro no inicia la ejecución. let futuro = ejemplo_tarea ();       // El `Futuro` solo se ejecuta cuando realmente lo sondeamos, a diferencia de Javascript. futuros :: ejecutor :: block_on ( futuro ); } 

En rápido

Swift 5.5 (2021) [33] agregó soporte para async/await como se describe en SE-0296. [34]

func  getNumber ()  lanzamientos asíncronos  -> Int { intenta esperar tarea . dormir ( nanosegundos : 1_000_000_000 ) regresar 42 }         Tarea  {  dejar  primero  =  intentar  esperar  obtener Número ()  dejar  segundo  =  intentar  esperar  obtener Número ()  imprimir ( primero  +  segundo ) }

Beneficios y críticas

El patrón async/await es especialmente atractivo para los diseñadores de lenguajes que no tienen ni controlan su propio tiempo de ejecución, ya que async/await se puede implementar únicamente como una transformación a una máquina de estados en el compilador. [35]

Los partidarios afirman que se puede escribir código asincrónico y sin bloqueo con async/await que se parece casi al código de bloqueo síncrono tradicional. En particular, se ha argumentado que await es la mejor manera de escribir código asincrónico en programas de paso de mensajes ; en particular, se citaron como beneficios pendientes estar cerca del código de bloqueo, la legibilidad y la cantidad mínima de código repetitivo . [36] Como resultado, async/await hace que sea más fácil para la mayoría de los programadores razonar sobre sus programas, y await tiende a promover un código sin bloqueo mejor y más robusto en las aplicaciones que lo requieren. [ dudoso ]

Los críticos de async/await señalan que el patrón tiende a hacer que el código circundante también sea asincrónico; y que su naturaleza contagiosa divide los ecosistemas de bibliotecas de los idiomas entre bibliotecas y API sincrónicas y asincrónicas, un problema a menudo denominado "coloración de funciones". [37] Las alternativas a async/await que no sufren este problema se denominan "incoloras". Ejemplos de diseños incoloros incluyen las rutinas de Go y los hilos virtuales de Java . [38]

Ver también

Referencias

  1. ^ abcdefg Skeet, Jon (23 de marzo de 2019). C# en profundidad . Manning. ISBN 978-1617294532.
  2. ^ "Anuncio de Rust 1.39.0" . Consultado el 7 de noviembre de 2019 .
  3. ^ "Lanzada la versión 0.9.4 - blog de Nim" . Consultado el 19 de enero de 2020 .
  4. ^ "Concurrencia: el lenguaje de programación Swift (Swift 5.5)". docs.swift.org . Consultado el 28 de septiembre de 2021 .
  5. ^ "Referencia del lenguaje Zig".
  6. ^ Syme, Don; Petricek, Tomás; Lomov, Dmitry (2011). "El modelo de programación asincrónica de F#". Aspectos prácticos de los lenguajes declarativos . Apuntes de conferencias sobre informática. vol. 6539. Enlace Springer. págs. 175–189. doi :10.1007/978-3-642-18378-2_15. ISBN 978-3-642-18377-5. Consultado el 29 de abril de 2021 .
  7. ^ "La historia temprana de F#, HOPL IV". Biblioteca Digital ACM . Consultado el 29 de abril de 2021 .
  8. ^ Hejlsberg, Anders. "Anders Hejlsberg: Presentación de Async: simplificación de la programación asincrónica". Canal 9 MSDN . Microsoft . Consultado el 5 de enero de 2021 .
  9. ^ "async: ejecute operaciones IO de forma asincrónica y espere sus resultados". Hackeo .
  10. ^ "Novedades de Python 3.5: documentación de Python 3.9.1". docs.python.org . Consultado el 5 de enero de 2021 .
  11. ^ Gaurav, Seth (30 de noviembre de 2015). "Anuncio de TypeScript 1.7". Mecanografiado . Microsoft . Consultado el 5 de enero de 2021 .
  12. ^ Matsakis, Niko. "¡Async-await en Rust estable! | Blog de Rust". blog.rust-lang.org . Blog de óxido . Consultado el 5 de enero de 2021 .
  13. ^ "Concurrencia: el lenguaje de programación Swift (Swift 5.6)".
  14. ^ abcde Albahari, José (2022). C# 10 en pocas palabras . O'Reilly. ISBN 978-1-098-12195-2.
  15. ^ "Presentación de flujos de trabajo asincrónicos de F#". 10 de octubre de 2007.
  16. ^ "Patrón asincrónico basado en tareas". Microsoft . Consultado el 28 de septiembre de 2020 .
  17. ^ Precio, Mark J. (2022). C# 8.0 y .NET Core 3.0: desarrollo multiplataforma moderno: cree aplicaciones con C#, .NET Core, Entity Framework Core, ASP.NET Core y ML.NET usando Visual Studio Code . Paquete. ISBN 978-1-098-12195-2.
  18. ^ Tepliakov, Sergey (11 de enero de 2018). "Ampliación de los métodos asíncronos en C#". Soporte para desarrolladores . Consultado el 30 de octubre de 2022 .
  19. ^ Stephen Cleary, Async/Await: mejores prácticas en programación asincrónica
  20. ^ "Python versión Python 3.5.0".
  21. ^ "PEP 492 - Corrutinas con sintaxis asíncrona y en espera".
  22. ^ "esperar - JavaScript (MDN)" . Consultado el 2 de mayo de 2017 .
  23. ^ "Guía de actualización de jQuery Core 3.0" . Consultado el 2 de mayo de 2017 .
  24. ^ "Domando a la bestia asincrónica con ES7" . Consultado el 12 de noviembre de 2015 .
  25. ^ Fundación, Node.js (30 de mayo de 2017). "Nodo v8.0.0 (actual) - Node.js". Nodo.js.
  26. ^ "El Comité ISO C++ anuncia que el diseño de C++20 ya tiene todas las funciones". 25 de febrero de 2019.
  27. ^ "Corrutinas (C++20)".
  28. ^ "s_task: biblioteca de rutinas esperada para C". GitHub .
  29. ^ "Future::AsyncAwait: sintaxis de subrutina diferida para futuros".
  30. ^ "Votos de subvenciones de septiembre de 2018: la Fundación Perl". noticias.perlfoundation.org . Consultado el 26 de marzo de 2019 .
  31. ^ Matsakis, Niko. "¡Async-await en Rust estable!". Blog de óxido . Consultado el 7 de noviembre de 2019 .
  32. ^ Oppermann, Philipp. "Asíncrono/Esperar" . Consultado el 28 de octubre de 2020 .
  33. ^ "Copia archivada". Archivado desde el original el 23 de enero de 2022 . Consultado el 20 de diciembre de 2021 .{{cite web}}: Mantenimiento CS1: copia archivada como título ( enlace )
  34. ^ "SE-0296". GitHub .
  35. ^ "Async Parte 3: cómo el compilador de C# implementa funciones asíncronas".
  36. ^ Liebre 'Sin bichos'. Ocho formas de manejar devoluciones sin bloqueo en programas de paso de mensajes CPPCON, 2018
  37. ^ "¿De qué color es tu función?".
  38. ^ "Hilos virtuales".