Back

Casos de Uso para Generadores en JavaScript

Casos de Uso para Generadores en JavaScript

Los generadores de JavaScript (function*) han sido parte del lenguaje desde ES2015, sin embargo, muchos desarrolladores frontend todavía recurren a arrays o cadenas de promesas cuando los generadores serían una opción más limpia. El valor principal no es la velocidad pura — es la evaluación diferida (lazy evaluation), composabilidad y control preciso sobre la iteración. Este artículo cubre dónde los generadores realmente se ganan su lugar en el código frontend moderno.

Puntos Clave

  • Los generadores producen valores bajo demanda mediante evaluación diferida, evitando computación innecesaria y arrays intermedios
  • La API Iterator Helpers proporciona métodos integrados map, filter y take para iteradores devueltos por generadores, eliminando la necesidad de funciones de utilidad personalizadas
  • yield* hace que el recorrido recursivo de árboles y grafos sea legible y diferido
  • Los generadores asíncronos (async function*) se combinan con for await...of para manejar la obtención de datos paginados o por lotes con gestión mínima de estado

Qué Hace Diferentes a los Generadores de JavaScript

Una función generadora devuelve un iterador. Llamar a la función no ejecuta ningún código — te entrega un objeto con un método .next(). Cada llamada a .next() ejecuta el cuerpo de la función hasta el siguiente yield, luego se pausa, preservando el estado local entre llamadas.

function* range(start, end) {
  for (let i = start; i < end; i++) yield i
}

for (const n of range(0, 5)) {
  console.log(n) // 0, 1, 2, 3, 4
}

Debido a que los generadores implementan el protocolo de iterador, funcionan directamente con for...of, sintaxis spread y desestructuración — no se necesita adaptador.

Iteración Diferida en JavaScript: Procesando Datos Sin Materializarlos

La razón principal para usar un generador en lugar de un array es la iteración diferida: los valores se producen solo cuando se solicitan. Esto importa cuando:

  • El conjunto de datos completo es grande y solo necesitas parte de él
  • Calcular cada valor es costoso
  • La secuencia es conceptualmente infinita
function* naturals() {
  let n = 0
  while (true) yield n++
}

// Solo calcula valores hasta el punto de interrupción
for (const n of naturals()) {
  if (n > 100) break
}

No se crea ningún array intermedio. No se calculan valores más allá del punto de interrupción.

La API Iterator Helpers: Pipelines Diferidos Integrados

Escribir utilidades personalizadas de map, filter y take solía ser código repetitivo necesario. La API Iterator Helpers — ahora disponible en todos los navegadores modernos — añade estos métodos directamente a los iteradores síncronos:

const result = naturals()
  .filter(n => n % 2 === 0)
  .map(n => n * n)
  .take(5)
  .toArray() // [0, 4, 16, 36, 64]

Cada paso es diferido. .toArray() es lo que desencadena la evaluación. Esto hace que los pipelines basados en generadores sean significativamente más limpios sin bibliotecas de terceros. Ten en cuenta que estos helpers se aplican a iteradores síncronos — los helpers de iteradores asíncronos aún no están estandarizados en todos los entornos.

Recorrido de Árboles y Grafos

Los generadores son una opción natural para recorrer estructuras recursivas. El recorrido en profundidad (depth-first) de un árbol similar al DOM se vuelve directo:

function* walkTree(node) {
  yield node
  for (const child of node.children ?? []) {
    yield* walkTree(child)
  }
}

for (const node of walkTree(rootNode)) {
  if (node.type === 'input') processInput(node)
}

yield* delega a un generador anidado, manteniendo la recursión legible y el recorrido diferido — te detienes tan pronto como encuentras lo que necesitas.

Generadores Asíncronos en JavaScript: Obtención de Datos Paginados y por Lotes

async function* extiende el patrón a secuencias asíncronas. Combinado con for await...of, es ideal para respuestas de API paginadas:

async function* fetchPages(url) {
  let nextUrl = url
  while (nextUrl) {
    const res = await fetch(nextUrl)
    const data = await res.json()
    yield data.items
    nextUrl = data.nextPage ?? null
  }
}

for await (const batch of fetchPages('/api/records')) {
  processBatch(batch)
}

Cada página se obtiene solo cuando el bucle avanza. No hay necesidad de recopilar todas las páginas por adelantado o gestionar el estado de paginación externamente — el generador lo mantiene.

Cuándo No Usar Generadores

Los generadores añaden indirección. Para una transformación simple de array que consumirás completamente, los métodos encadenados de array son más claros. Usa generadores cuando la secuencia sea grande, potencialmente infinita, o cuando necesites detenerte temprano sin computación desperdiciada.

Conclusión

Los generadores de JavaScript brillan en tres áreas: iteración diferida sobre secuencias grandes o infinitas, pipelines de datos componibles (especialmente con la API Iterator Helpers), y obtención de datos asíncrona donde necesitas control secuencial y con estado. No son un reemplazo para arrays o async/await — son la herramienta correcta cuando necesitas producir valores bajo demanda en lugar de todos a la vez.

Preguntas Frecuentes

Sí. Los generadores funcionan bien para producir secuencias de datos que los componentes de React consumen. Puedes llamar a un generador dentro de un hook useEffect o useMemo para calcular valores de forma diferida. Sin embargo, no uses un generador como la función del componente en sí — React espera que los componentes devuelvan JSX, no iteradores.

El generador permanece pausado en su último punto de yield. Se vuelve elegible para la recolección de basura una vez que no quedan referencias a su objeto iterador. Si necesitas que la lógica de limpieza se ejecute cuando la iteración se detiene temprano, envuelve el yield en un bloque try-finally. El bloque finally se ejecuta cuando se llama al método return del iterador o cuando el generador es recolectado por el garbage collector.

Para colecciones pequeñas que se consumen completamente, los generadores tienen una ligera sobrecarga del mecanismo de pausa y reanudación. La diferencia de rendimiento es insignificante en la mayoría de las aplicaciones. Los generadores se vuelven más rápidos en la práctica cuando procesas grandes conjuntos de datos parcialmente, porque evitan asignar arrays intermedios y omiten la computación de valores que nunca solicitas.

Un generador asíncrono produce valores incrementalmente a medida que están disponibles, mientras que un enfoque basado en Promise espera hasta que todos los datos estén recopilados antes de devolver. Esto significa que los generadores asíncronos te permiten comenzar a procesar el primer lote de resultados inmediatamente, reducir el uso máximo de memoria y darte un control más fino sobre cuándo ocurre cada obtención subsiguiente.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay