Funciones generadoras en JavaScript
En este artículo, explicaremos las funciones generadoras en JavaScript.
YouTube Video
Funciones generadoras en JavaScript
Las funciones generadoras de JavaScript son funciones especiales que se diferencian de las funciones regulares en que pueden realizar ejecución diferida y pausar y reanudar. Al comprender las funciones generadoras, puedes manejar de manera eficiente las operaciones asíncronas y el procesamiento secuencial de grandes cantidades de datos. Aquí proporcionaremos una explicación detallada de cómo funcionan las funciones generadoras, cómo utilizarlas y casos prácticos de uso.
¿Qué son las funciones generadoras?
Una función generadora se define con function*
(una definición de función con un asterisco) y, a diferencia de las funciones regulares, puede pausar la ejecución a mitad del camino y reanudarla mientras mantiene su estado. Las funciones generadoras permiten "ejecución diferida" y devuelven resultados de manera incremental, lo que habilita una gestión eficiente de la memoria y el procesamiento secuencial.
Sintaxis
1function* generatorFunction() {
2 yield 'First value';
3 yield 'Second value';
4 return 'Done';
5}
De esta manera, una función generadora puede tener múltiples expresiones yield
y usar yield
para pausar su ejecución. Cuando se llama a una función generadora, el cuerpo de la función no se ejecuta inmediatamente y se devuelve un objeto generador. Puedes llamar al método next()
en este objeto para reanudar la función desde el punto en que se pausó.
Uso básico de las funciones generadoras
A continuación, veamos un ejemplo básico del uso de funciones generadoras.
1function* simpleGenerator() {
2 yield 1;
3 yield 2;
4 yield 3;
5}
6
7const gen = simpleGenerator();
8
9console.log(gen.next()); // { value: 1, done: false }
10console.log(gen.next()); // { value: 2, done: false }
11console.log(gen.next()); // { value: 3, done: false }
12console.log(gen.next()); // { value: undefined, done: true }
Un punto clave a destacar aquí es que el generador devuelve valores cada vez con yield
, y mientras la propiedad done
sea false
, indica que hay más valores por venir. La última llamada a next()
devuelve done: true
, indicando que el generador ha completado.
La palabra clave yield
y los valores de pausa
yield
es una palabra clave que indica un punto de pausa dentro de una función generadora. El valor a la derecha de yield
se devuelve cuando se llama a next()
. Además, yield
permite la comunicación bidireccional. En otras palabras, al pasar un valor como argumento al método next()
, ese valor se envía a la función generadora.
1function* generatorWithYield() {
2 const value1 = yield 'First yield';
3 console.log('Received value:', value1);
4 const value2 = yield 'Second yield';
5 console.log('Received value:', value2);
6}
7
8const gen = generatorWithYield();
9
10console.log(gen.next()); // { value: 'First yield', done: false }
11console.log(gen.next('Apple')); // Received value: Apple
12 // { value: 'Second yield', done: false }
13console.log(gen.next('Banana'));// Received value: Banana
14 // { value: undefined, done: true }
En este ejemplo, la llamada next('Apple')
envía el valor 'Apple'
a la función generadora, donde se utiliza dentro de la función.
Gestión del estado del generador
Los generadores pueden mantener su estado de ejecución, lo que permite una representación concisa de bucles largos o procesamiento secuencial. El siguiente ejemplo demuestra un generador que produce números indefinidamente.
1function* infiniteGenerator() {
2 let i = 0;
3 while (true) {
4 yield i++;
5 if (i > 10) {
6 break;
7 }
8 }
9}
10
11const gen = infiniteGenerator();
12
13console.log(gen.next().value); // 0
14console.log(gen.next().value); // 1
15console.log(gen.next().value); // 2
16// Continues...
Este generador produce números continuamente utilizando un bucle while(true)
, lo que te permite obtener valores según sea necesario. Esto permite un procesamiento eficiente de grandes conjuntos de datos.
Aplicaciones de las funciones generadoras
Las funciones generadoras son adecuadas para ejecutar múltiples procesos de manera secuencial. Por ejemplo, son útiles para procesar solicitudes de API de forma secuencial o dividir y procesar archivos grandes.
1function* apiRequestGenerator() {
2 yield fetch('https://codesparklab.com/json/example1.json');
3 yield fetch('https://codesparklab.com/json/example2.json');
4 yield fetch('https://codesparklab.com/json/example3.json');
5}
Por lo tanto, los generadores, que se pueden usar para el procesamiento asíncrono, son muy beneficiosos para el manejo secuencial eficiente de datos.
Generadores asincrónicos
Los generadores asíncronos, introducidos en ES2018, permiten devolver valores asíncronos secuencialmente combinando async
y yield
. Esto hace posible escribir de manera concisa el procesamiento asíncrono en combinación con await
.
Sintaxis
1async function* asyncGenerator() {
2 yield await Promise.resolve(1);
3 yield await Promise.resolve(2);
4 yield await Promise.resolve(3);
5}
6
7const gen = asyncGenerator();
8
9(async () => {
10 for await (const value of gen) {
11 console.log(value); // 1, 2, 3
12 }
13})();
Los generadores asíncronos pueden recuperar valores secuencialmente utilizando un bucle for await...of
. Este patrón es particularmente útil al manejar flujos de datos asíncronos.
Ejemplo práctico: Simplificando el procesamiento asincrónico con generadores
Las funciones generadoras también se utilizan para simplificar el flujo del procesamiento asíncrono. Por ejemplo, al combinar yield
y Promise
como se muestra a continuación, puedes escribir operaciones asíncronas que parecen ser síncronas.
1function* asyncFlow() {
2 const data1 = yield fetch('https://codesparklab.com/json/example1.json');
3 console.log(data1);
4 const data2 = yield fetch('https://codesparklab.com/json/example2.json');
5 console.log(data2);
6}
7
8const gen = asyncFlow();
9
10function handleAsync(generator) {
11 const next = (promise) => {
12 promise.then((result) => {
13 const { value, done } = generator.next(result);
14 if (!done) {
15 next(value);
16 }
17 });
18 };
19
20 next(generator.next().value);
21}
22
23handleAsync(gen);
Este código realiza solicitudes a una API de manera secuencial utilizando un generador y procesa los resultados.
Resumen
Las funciones generadoras son una de las características más potentes de JavaScript, ya que poseen la capacidad de pausar y reanudar la ejecución de una función. Esto permite el procesamiento secuencial, el procesamiento asíncrono y la manipulación eficiente de grandes conjuntos de datos. Comprender los generadores es un paso importante para dominar técnicas avanzadas en JavaScript.
Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.