Funciones generadoras

Funciones reanudables


Además de las funciones que hemos visto hasta ahora, entre las que se encuentran funciones anónimas, callbacks, clausuras y arrow functions, existe un tipo de función menos conocido que son los generadores o funciones generadoras.

¿Qué es una función generadora?

Básicamente, una función generadora es una función que se puede pausar y reanudar, y por lo tanto, nos puede devolver múltiples valores. Puede parecer un concepto un poco extraño, pero se entiende muy facilmente con algo de código.

Observa el siguiente ejemplo. El primer detalle que encontraremos para diferenciar una función generadora de una función tradicional es que tiene un asterisco al final de function:

function* generator() {
  // code
  yield "One";
  // code
  yield "Two";
  // code
  yield "Three";
}

const values = generator();

Luego, observa que en lugar de llamar a return para devolver un valor, utilizamos yield. Es algo muy similar, sólo que en lugar de devolver el valor y terminar la ejecución, lo pausamos.

Pausando y reanudando la función

Sin embargo, hasta ahora, aunque lo parezca, no hemos ejecutado ninguna línea de código de nuestra función generadora, sólo la hemos preparado para empezar a ejecutar.

values.next();   // { value: 'One', done: false }
values.next();   // { value: 'Two', done: false }
values.next();   // { value: 'Three', done: false }
values.next();   // { value: undefined, done: true }

Observa que lo que hacemos es ejecutar un método o función llamado .next() en nuestra variable values. Esto lo que hará es ejecutar nuestra función generadora hasta que encuentre un yield, momento en el que pausará la ejecución y devolverá un objeto con el valor devuelto en la propiedad value y un valor booleano en done que indica si se ha terminado de ejecutar la función. Observa que done no es verdadero hasta devolver el último valor y volver a ejecutar .next().

En nuestro caso no hemos colocado código intermedio entre los yield, pero donde hemos colocado los comentarios existirían líneas de código. Los yield son una forma de colocar puntos de control para pausar la ejecución de nuestra función, y retomarla cuando necesitemos.

Ten en cuenta que en una función generadora también podemos utilizar return:

function* generator() {
  console.log("First part");
  yield "One";
  return "Two";
  yield "Three";
}

values.next();   // { value: 'One', done: false }
values.next();   // { value: 'Two', done: true }

Sin embargo, en este caso, return devolverá el valor pero al contrario que yield finalizará la ejecución de nuestra función, dándola por terminada como en una función tradicional.

Iteración de valores

Podemos iterar directa y facilmente los valores de nuestra función generadora utilizando el operador spread. Observa el siguiente código, con la función generadora generator():

function* generator() {
  yield "First";
  yield "Second";
  yield "Third";
}

const values = [...generator()];   // [ 'First', 'Second', 'Third' ]

En este caso, hemos iterado los valores devueltos por yield, guardándolos en un array, ya que estamos volviendo a estructurar en un array los valores devueltos.

Esto sería muy parecido a lo que podríamos hacer con un bucle for...of, o incluso con una función .forEach(), .map() o similar, donde se procesaría en cada iteración los valores devueltos por el yield:

for (const value of generator()) {
  console.log(value);
}

// Equivalente al anterior
generator().forEach(value => console.log(value));

Recuerda que aunque en estos ejemplos no se muestra, una de las características estrella de las funciones generadores es que podemos colocar código entre los yield. De esta forma, además de devolver un valor con yield (que puede depender de nuestro código), estamos ejecutando código adicional que puede realizar otras tareas.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev