paint-brush
Async/Await en Golang: una guía introductoriapor@hasantalks
81,465 lecturas
81,465 lecturas

Async/Await en Golang: una guía introductoria

por MD Ahad Hasan4m2020/11/28
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

Async/Await es una función de lenguaje que proporciona una interfaz más sencilla para la programación asíncrona. Golang es un lenguaje de programación concurrente con potentes funciones como Goroutines y Channels. Podemos implementarlo con una estructura e interfaz simples en Golang usando las palabras clave "async/await" y "goroutines". El código completo está disponible en el enlace del proyecto. Para implementarlo, comenzaremos con un directorio de paquetes llamado 'async' y "future" en el main.
featured image - Async/Await en Golang: una guía introductoria
MD Ahad Hasan HackerNoon profile picture

Golang es un lenguaje de programación concurrente. Tiene características poderosas como

 Goroutines
y
 Channels
que puede manejar tareas asincrónicas muy bien. Además, las goroutines no son subprocesos del sistema operativo, y es por eso que puede activar tantas goroutines como desee sin mucha sobrecarga, su tamaño de pila comienza en solo 2 KB . Entonces por qué
 async/await
? Async/Await es una buena función de lenguaje que proporciona una interfaz más sencilla para la programación asíncrona.

Enlace del proyecto: https://github.com/Joker666/AsyncGoDemo

¿Como funciona?

Comenzó con F# y luego C#, ahora en Python y Javascript, async/await es una característica extremadamente popular de un lenguaje. Simplifica la estructura de ejecución del método asíncrono y se lee como código síncrono. Mucho más fácil de seguir para los desarrolladores. Veamos un ejemplo simple en C# de cómo funciona async/await

 static async Task Main(string[] args) { Console.WriteLine( "Let's start ..." ); var done = DoneAsync(); Console.WriteLine( "Done is running ..." ); Console.WriteLine( await done); } static async Task<int> DoneAsync() { Console.WriteLine( "Warming up ..." ); await Task.Delay( 3000 ); Console.WriteLine( "Done ..." ); return 1 ; }

tenemos el

 Main
función que se ejecutaría cuando se ejecute el programa. Tenemos
 DoneAsync
que es una función asíncrona. Detenemos la ejecución del código con
 Delay
función durante 3 segundos. Delay es una función asíncrona en sí misma, por lo que la llamamos con await.

await solo bloquea la ejecución del código dentro de la función asíncrona

En la función principal, no llamamos

 DoneAsync
con espera. Pero la ejecución comienza para
 DoneAsync
. Solo cuando lo esperamos, recuperamos el resultado. El flujo de ejecución se ve así

 Let 's start ... Warming up ... Done is running ... Done ... 1

Esto parece increíblemente simple para la ejecución asíncrona. Veamos cómo podemos hacerlo con Golang usando Goroutines y Channels

 func DoneAsync () chan int { r := make ( chan int ) fmt.Println( "Warming up ..." ) go func () { time.Sleep( 3 * time.Second) r <- 1 fmt.Println( "Done ..." ) }() return r } func main () { fmt.Println( "Let's start ..." ) val := DoneAsync() fmt.Println( "Done is running ..." ) fmt.Println(<- val) }

Aquí,

 DoneAsync
se ejecuta de forma asíncrona y devuelve un canal. Escribe un valor en el canal una vez que termina de ejecutar la tarea asíncrona. En
 main
función, invocamos
 DoneAsync
y seguimos haciendo nuestras operaciones y luego leemos el valor del canal devuelto. Es una llamada de bloqueo que espera hasta que el valor se escribe en el canal y después de que obtiene el valor lo escribe en la consola.

 Let 's start ... Warming up ... Done is running ... Done ... 1

Vemos, logramos el mismo resultado que el programa C# pero no se ve tan elegante como async/await. Si bien esto es realmente bueno, podemos hacer muchas cosas más granulares con este enfoque con mucha facilidad, también podemos implementar palabras clave asíncronas/en espera en Golang con una estructura y una interfaz simples. Probemos eso.

Implementando Async/Await

El código completo está disponible en el enlace del proyecto. Para implementar async/await en Golang, comenzaremos con un directorio de paquetes llamado

 async
. La estructura del proyecto parece

 . ├── async │  └── async .go ├── main .go └── README.md

En el archivo asíncrono, escribimos la interfaz futura más simple que puede manejar tareas asíncronas.

 package async import "context" // Future interface has the method signature for await type Future interface { Await() interface {} } type future struct { await func (ctx context.Context) interface {} } func (f future) Await () interface {} { return f.await(context.Background()) } // Exec executes the async function func Exec (f func () interface {}) Future { var result interface {} c := make ( chan struct {}) go func () { defer close (c) result = f() }() return future{ await: func (ctx context.Context) interface {} { select { case <-ctx.Done(): return ctx.Err() case <-c: return result } }, } }

No está sucediendo mucho aquí, agregamos un

 Future
interfaz que tiene la
 Await
firma del método. A continuación, agregamos un
 future
estructura que contiene un valor, una firma de función de la
 await
función. Ahora
 futute
implementos de estructura
 Future
el método Await de la interfaz invocando su propio
 await
función.

A continuación en el

 Exec
función, ejecutamos la función pasada de forma asíncrona en goroutine. Y le devolvemos el
 await
función. Espera a que el canal se cierre o el contexto para leer. Según lo que suceda primero, devuelve el error o el resultado, que es una interfaz.

Ahora armado con este nuevo paquete asíncrono, veamos cómo podemos cambiar nuestro código go actual

 func DoneAsync () int { fmt.Println( "Warming up ..." ) time.Sleep( 3 * time.Second) fmt.Println( "Done ..." ) return 1 } func main () { fmt.Println( "Let's start ..." ) future := async.Exec( func () interface {} { return DoneAsync() }) fmt.Println( "Done is running ..." ) val := future.Await() fmt.Println(val) }

A primera vista, parece mucho más limpio, no estamos trabajando explícitamente con goroutine o canales aquí. Nuestro

 DoneAsync
La función ha sido cambiada a una naturaleza completamente síncrona. En la función principal, usamos el
 async
paquete
 Exec
método para manejar
 DoneAsync
. que inicia la ejecución de
 DoneAsync
. El flujo de control se devuelve a
 main
función que puede ejecutar otras piezas de código. Finalmente, hacemos una llamada de bloqueo a
 Await
y volver a leer los datos.

Ahora el código se ve mucho más simple y fácil de leer. Podemos modificar nuestro paquete asíncrono para incorporar muchos otros tipos de tareas asíncronas en Golang, pero por ahora nos limitaremos a una implementación simple en este tutorial.

Conclusión

Hemos revisado qué async/await it e implementamos una versión simple de eso en Golang. Le animo a que busque mucho más en async/await y vea cómo puede facilitar la legibilidad del código base mucho mejor.