Node.js Generators y comparar con devoluciones de llamada
⚡ Resumen inteligente
GeneratorEn Node.js, las funciones se pueden suspender y reanudar bajo demanda. La palabra clave `yield` pausa la función, el método `next` la reanuda, y esta combinación simplifica las funciones de devolución de llamada anidadas, convirtiéndolas en código secuencial legible.

En este tutorial analizaremos los generadores y en qué se diferencian de las funciones de devolución de llamada.
¿Qué son los generadores?
Generators se hizo popular en Node.js por lo que son capaces de hacer.
- GeneratorLas s son ejecuciones de funciones que pueden suspenderse y reanudarse en un momento posterior.
- GeneratorLas funciones son útiles para conceptos como la "ejecución diferida". Al suspender la ejecución y reanudarla cuando se desee, los valores se obtienen solo cuando son necesarios.
Generators tienen los dos métodos clave que se describen a continuación.
- método de rendimiento — llamada dentro de una función para detener la ejecución en la línea exacta donde
yield. - Siguiente método — llamada desde la aplicación principal para reanudar una función que ha cedido el control. La ejecución continúa hasta la siguiente
yieldo hasta el final de la función.
Veamos un ejemplo de cómo se pueden usar los generadores. El ejemplo define una función de suma simple que suma dos números, deteniendo la ejecución en diferentes puntos para mostrar cómo se comportan los generadores.
function* Add(x) { yield x + 1; // The value passed into the second next() call lands here var y = yield(null); return x + y; } var gen = Add(5); gen.next(); // runs to the first yield, returns { value: 6, done: false } gen.next(6); // resumes, y becomes 6, returns { value: 11, done: true }
Code Explicación
- El primer paso es definir la función generadora. Observe el
*añadido a lafunctionpalabra clave. La función se llamaAddy toma un parámetrox. - El
yieldLa palabra clave es específica de los generadores y pausa la función a mitad de la ejecución. La ejecución se detiene aquí hastanext()se invoca. - El segundo
yieldambas pausan la función y reciben un valor: lo que sea que se pase a lo siguientenext()La llamada se convierte en el valor dey. - llamar
gen.next()La primera vez ejecuta la función hasta el primero.yieldLlamandogen.next(6)lo reanuda, asigna 6 ayy vuelvex + y.
⚠️ Corrección: El listado original contenía la línea y = 6 inmediatamente despues var y = yield(null);Esa asignación sobrescribe cualquier valor. next() suministros, lo cual frustra el propósito del ejemplo y hace que el segundo yield sin sentido. El valor ahora se pasa a través de gen.next(6), que es como un generador recibe datos del emisor.
Callbacks frente a generadores
Generators resuelve el problema conocido como infierno de las devoluciones de llamada. Las funciones de devolución de llamada a veces se anidan tan profundamente durante el desarrollo de una aplicación Node.js que son difíciles de seguir.
Aquí es donde los generadores resultan útiles. Uno de los ejemplos más comunes es la creación de funciones de temporizador. El siguiente ejemplo crea una función de retardo simple y luego la llama con retardos de 1000, 2000 y 3000 ms.
Paso 1) Defina la función de devolución de llamada con el código de retardo de tiempo necesario.
function Timedelay(ptime, callback) { setTimeout(function() { callback("Pausing for " + ptime); }, ptime); // the delay must use ptime, the parameter name }
Code Explicación
- Una función llamada
Timedelayse crea con un parámetroptime, que contiene el número de milisegundos durante los cuales se debe hacer la pausa. setTimeoutEspera esa cantidad de milisegundos y luego invoca la función de devolución de llamada proporcionada.
⚠️ Corrección: El código original pasó time como el segundo argumento a setTimeout, pero ninguna variable llamada time existe — el parámetro es ptimeTal como está escrito, arroja ReferenceError: time is not defined.
Paso 2) Ahora consideremos el código que utiliza funciones de devolución de llamada. Para encadenar retrasos de 1000, 2000 y 3000 milisegundos, las funciones de devolución de llamada se anidan unas dentro de otras.
Timedelay(1000, function(message) { console.log(message); Timedelay(2000, function(message) { console.log(message); Timedelay(3000, function(message) { console.log(message); }); }); });
Code Explicación
TimedelaySe llama como una función de devolución de llamada con el valor 1000.- Dentro de esa devolución de llamada,
TimedelaySe vuelve a llamar con 2000. - Dentro de ese,
TimedelaySe llama por tercera vez con 3000.
⚠️ Corrección: cada devolución de llamada en el registro original msg, pero el parámetro se llama messageLas tres líneas lanzaron ReferenceError: msg is not defined.
Como se puede observar en el código anterior, la situación se complica con cada llamada adicional; este anidamiento es lo que describe el "infierno de las devoluciones de llamada".
Paso 3) Ahora veamos el mismo comportamiento escrito con generadores. La secuencia se lee como tres líneas planas en lugar de tres niveles de anidamiento.
function* Messages() { console.log(yield Timedelay(1000)); console.log(yield Timedelay(2000)); console.log(yield Timedelay(3000)); }
Code Explicación
- Se define una función generadora para llamar a la
Timedelayfunción. yieldse usa conTimedelayy 1000 como valor del parámetro.yieldSe vuelve a usar con 2000, y una vez más con 3000.
⚠️ Corrección: El original escribió console,log(...) con una coma en lugar de un punto en las tres líneas, lo cual es un error de sintaxis e impide que el archivo se analice correctamente.
Cómo ejecutar un Generator con función de corredor
El generador anterior se lee de maravilla, pero no hará nada por sí solo. Este es el detalle que la mayoría de las explicaciones omiten, y es la razón por la que la versión plana puede parecer magia. Un generador es una máquina en pausa, y algo tiene que seguir presionando el botón. Llamando Messages() Simplemente crea el objeto generador y regresa inmediatamente; nunca se inicia ningún temporizador y no se imprime nada.
La pieza que falta es un corredor: una pequeña función que llama next()espera a que finalice el trabajo asíncrono y luego llama next() de nuevo con el resultado.
// Timedelay now hands its result back to the runner function Timedelay(ptime) { return function(resume) { setTimeout(function() { resume("Pausing for " + ptime); }, ptime); }; } // The runner drives the generator to completion function run(generatorFunction) { var it = generatorFunction(); function step(value) { var result = it.next(value); if (result.done) return; // result.value is the function returned by Timedelay result.value(step); } step(); } run(Messages);
Tres puntos hacen que esto funcione. Primero, Timedelay Ya no acepta una devolución de llamada directamente; devuelve una función que el ejecutor puede invocar, devolviendo el control cuando se activa el temporizador. Segundo, it.next(value) alimenta el resultado anterior al generador en pausa, que es lo que permite yield aparecer en el lado derecho de una asignación. Tercero, el corredor se detiene tan pronto como result.done se vuelve verdadero, por lo que la recursión termina naturalmente. Este patrón de corredor es exactamente lo que proporcionaron bibliotecas como co, y es el mecanismo que async/await Posteriormente, fue absorbido por el propio idioma.
Generators vs async/await: ¿Cuál usar?
GeneratorLas s eran la forma estándar de escribir código asíncrono plano antes async/await existían, y ambos comparten la misma idea subyacente de pausar una función a mitad de camino. Node ha sido compatible async/await De forma nativa desde la versión 7.6, lo que cambia la recomendación para cualquiera que comience un nuevo trabajo hoy.
| Aspecto | Generators | async / await |
|---|---|---|
| Se necesita corredor | Sí, escrito por usted o por una biblioteca. | No, está integrado en el lenguaje. |
| Palabra clave de pausa | yield |
await |
| Manejo de errores | El corredor debe reenviar los errores | Intentar atrapar lo normal |
| Puede hacer una pausa a mitad de camino. | Sí, y puede reanudarse de forma selectiva. | No, se ejecuta hasta completarse |
| Mejores para | Secuencias perezosas y flujos infinitos | Trabajo asíncrono ordinario |
Usar async/await Para el flujo asíncrono en código nuevo, ya que elimina el ejecutor y restaura el try y catch habituales. Mantenga los generadores para lo que solo ellos hacen bien: producir valores de forma diferida, iterar secuencias grandes o infinitas y cualquier caso en el que el consumidor decida exactamente cuándo se calcula el siguiente valor.
Preguntas frecuentes sobre bancarrota Generator Errores y cómo solucionarlos
La mayoría de los problemas con los generadores provienen de olvidar que la función es inerte hasta que algo la llama. next()o bien, debido a pequeños errores sintácticos alrededor del asterisco. Cada síntoma que aparece a continuación indica su causa y su solución.
- No ocurre nada cuando se llama al generador: Llamarla devuelve un iterador, no un resultado. Llamar
next()o pasárselo a un corredor. - SyntaxError en yield: Falta el asterisco.
yieldes válido solo dentro de unfunction*. - El valor de next() llega indefinido: la primera
next()La llamada no puede entregar un valor, porque el generador no ha alcanzado unyieldSin embargo, envíe los datos a partir de la segunda llamada. - Bucle infinito en un corredor:
result.donenunca se comprueba, así quenext()Se sigue llamando incluso después de que termine. Siempre regresa cuando done sea verdadero. - Los errores desaparecen silenciosamente: Un rechazo dentro del paso asíncrono nunca se devuelve al generador. Reenvíelo con
it.throw(err)para que un bloque try-catch escrito dentro del generador pueda verlo y manejarlo.




