intive Argentina Blog

Generadores ES6

Iniciaremos este artículo explicando el concepto de “generadores”, una nueva característica que incluye ECMAScript al lenguaje por medio de su nuevo estándar, ES6. Un generador es una función, declarándose casi de manera similar que una función convencional en Javascript, salvo por un pequeño cambio en su sintaxis. La particularidad de esta nueva característica es el poder salir del generador e ingresar nuevamente al mismo cuando lo deseamos.

En cuanto a las ventajas, el uso de generadores nos conduce a un código más claro y puede prevenirnos del conocido callback hell. Una solución a este problema es el uso de Promises, aunque no nos permita deshacernos totalmente de las callback. Entonces, podemos celebrar: ¡los generadores llegan para hacernos olvidar de las callbacks en ese sentido!

Resumiendo, entonces, tenemos la posibilidad de pausar la ejecución del generador y que se el código siga ejecutándose fuera de éste. Después, le “decimos” que vuelva a ejecutarse desde el punto en el que lo pausamos.

En limpio: podemos suspenderlo y volver a entrar a esta función generadora cuando tengamos la intención de hacerlo.

En tren de entender este proceso un poco mejor, veremos un poco de código.

 

Sintaxis y funcionamiento

En cuanto a la sintaxis, entre las novedades podemos señalar la incorporación de nuevas palabras reservadas para el lenguaje, tanto para crear una función generadora como para determinar las suspensiones del mismo. Veamos cómo se procede a definirlo:

Se puede poner el * (asterisco) tanto al final de la keyword function o delante del nombre de la función generadora. Por convención, se escribe function*. Un generador puede estar en estado suspended o closed.

  • Dentro de la función generadora, utilizaremos la keyword yield. Con esta nueva palabra reservada, le “decimos” a la función: “Ejecutá esta/s línea/s de código y pausá el generador”. Esto quiere decir que yield suspende la ejecución del generador para poder continuarla en otro momento.
  • Los generadores aceptan parámetros en cualquier momento. (Tanto en la definición del generador como en la invocación del método next, gen.next(param)):

En este ejemplo estamos creando una función generadora que tiene dos pausas. Es necesario obtener una instancia del generador, por lo tanto, la variable gen hace una llamada a generator Function, que va a devolver un objeto Generator (generador) suspendido. Una vez que se llama al método .next(), el llamado de la función se congela y se ejecuta hasta que llegue hasta la próxima expresión yield. La primera llamada a .next() que ejecutamos nos devuelve lo siguiente:

La propiedad value es el resultado de ejecutar las operaciones propias del yield. La propiedad done es una forma que tiene el generador de indicarnos cuando termina de ejecutar todas las sentencias.

La próxima llamada al método .next() va a imprimir el resultado de la sentencia anterior y devuelve un objeto indicando el estado de esa operación:

Ahora bien, la última llamada al generador nos devolverá un objeto con el campo value como undefined, ya que no retorna ningún valor y done con el valor true, indicándonos que el generador ha terminado de ejecutar todas las sentencias dentro del mismo. Desde este momento, cada vez que llamemos al método .next() obtendremos el mismo objeto:

  • Como vimos, hicimos tres invocaciones al método .next() a pesar de tener dos yield. Lo que hay que tener en cuenta es que la cantidad de llamadas al método .next() es la cantidad de expresiones yield en nuestro generador más uno, es decir, hacemos yield+1 invocaciones a .next().

Finalmente, si hacemos una inspección a la instancia del generador que habíamos creado (la variable gen), va a indicarnos que el estado del generador es closed, es decir, todas las sentencias del generador han sido ejecutadas. El resultado sería:

 

Un uso interesante de generadores

Un uso práctico que podemos darles a los generadores es evitar las callbacks (como se mencionó) una vez que obtenemos el valor resultante de una Promise. Es decir, vamos a tener un código más simple y reducir notablemente las llamadas al método .then().

En este caso, emplearemos una librería llamada “co” para el ejemplo, al cual le vamos a pasar un generador y al final nos devolverá una Promise terminada la ejecución completa del generador. Además, lo compararemos con lo que sucedería si solamente utilizáramos Promises:

 

Conclusión

A modo de resumen, entonces, un generador es una función que puede ser suspendida y volver a ejecutarse a partir del último punto de salida. Se agrega una nueva palabra reservada yield y se nos provee una forma de declarar generadores con la expresión function*.

Podemos decir entonces que los generadores son un paso adelante de las Promises, dado que nos proveen una forma más clara y limpia de generar código asincrónico a como lo veníamos haciendo. Para otro artículo quedarán temas como los Iterators en generadores.

Actualmente hay ciertos navegadores que no soportan el uso de generadores. Babel, entre otros, nos va a dar la solución para aquellos que desean experimentar o aplicar estas nuevas features a su código.

Mirando al futuro de Javascript, llegará una nueva característica al lenguaje conocida como async/await, que se basa en un concepto similar a los generadores, de suspender y volver a ejecutar a partir del último punto de salida. La sintaxis que se propone con async/await viene a simplificar aún más el código que generamos habitualmente

Para quienes deseen indagar más generadores, les dejamos esta serie de links:

Mariano Furriel

Es desarrollador en la empresa desde marzo de 2016 e integra la Brigada Frontend. Cursa la carrera de Ingeniería Informática en la Universidad Argentina de la Empresa (UADE). Siempre está atento a leer y aprender  sobre nuevas tecnologías que permitan mejorar los diseños de las aplicaciones.

Deja un comentario