Skip to main content

Colecciones perezosas en JavaScript

⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.


r8vnhill/

En JavaScript, la evaluación perezosa nos permite generar valores solo cuando los necesitamos. Esto es útil cuando trabajamos con secuencias grandes o incluso infinitas, evitando que el programa consuma más memoria de la necesaria. Una de las formas más prácticas de lograr esto es con generadores.

📌 ¿Qué es un generador?

Un generador es básicamente una función especial que puede pausar y reanudar su ejecución. Se define con function* y usa la palabra clave yield para devolver valores de manera perezosa.

function* evenNumbers() {
let num = 0;
while (true) {
yield num;
num += 2;
}
}
¿Qué acabamos de hacer?

Aquí evenNumbers es un generador infinito de números pares. Cada vez que llamamos a next(), la función se pausa en yield num y espera hasta que pidamos el siguiente valor. Así evitamos generar números innecesarios.

🚀 ¿Cómo usamos un generador?

Para obtener los primeros n números pares, basta con usar un bucle y llamar a next() en cada iteración:

const evenGen = evenNumbers();
const firstTenEvens = [];

for (let i = 0; i < 10; i++) {
firstTenEvens.push(evenGen.next().value);
}

console.log(firstTenEvens); // [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Cada llamada a next() nos da el siguiente número de la secuencia, y el generador sigue funcionando hasta que decidimos detenerlo.

⚡ Generadores asíncronos en JavaScript

JavaScript también nos permite trabajar con flujos de datos asíncronos usando generadores asíncronos (async function*). Estos son muy útiles para manejar datos que llegan de una API o de un archivo sin bloquear la ejecución.

async function* asyncNumbers() {
let num = 0;
while (num < 10) {
await new Promise(resolve => setTimeout(resolve, 1000)); // Espera 1 segundo
yield num++;
}
}

(async () => {
for await (const num of asyncNumbers()) {
console.log(num);
}
})();
¿Qué acabamos de hacer?

Aquí asyncNumbers emite un número cada segundo, pausando su ejecución con await. Luego usamos for await...of para iterar sobre los valores sin necesidad de materializarlos todos en memoria.

📊 Resumen comparativo

Si comparamos JavaScript y Kotlin en cuanto a evaluación perezosa, encontramos diferencias importantes:

CaracterísticaJavaScriptKotlin
Definición de secuenciasUsa function* para crear generadores.Usa sequence {} o generateSequence().
Encadenamiento de operacionesNo soporta map o filter en generadores sin convertirlos en arrays.Soporta operaciones como map, filter y take sin crear colecciones intermedias.
AsincroníaPermite generadores asíncronos (async function*).Usa Flow para manejar flujos de datos asincrónicos.
Flujos infinitosPuede generar secuencias infinitas sin problemas.Permite lo mismo, pero con tipado seguro y mayor control.

✅ Beneficios y limitaciones

Beneficios

  • JavaScript es más flexible: Su sintaxis con function* es sencilla y fácil de entender.
  • Generadores asíncronos: Puedes manejar datos asíncronos de manera elegante.
  • Ideal para flujos infinitos: Solo genera los valores cuando se necesitan.

Limitaciones

  • No puedes encadenar map o filter directamente: Hay que convertir el generador en un array antes.
  • Menos seguridad de tipos: Puede generar errores en tiempo de ejecución que en Kotlin se evitarían en compilación.
  • Mayor riesgo de errores en asincronía: Manejar flujos asíncronos con generadores puede ser complicado.

🎯 Conclusiones

Los generadores en JavaScript ofrecen una forma poderosa de trabajar con secuencias perezosas, permitiendo producir valores solo cuando se necesitan. Esto los hace ideales para flujos de datos grandes o infinitos, optimizando el uso de memoria y mejorando el rendimiento en ciertos escenarios.

Sin embargo, su uso tiene algunas limitaciones, especialmente en comparación con las secuencias en Kotlin. La falta de encadenamiento directo de transformaciones (map, filter, etc.) y la menor seguridad de tipos pueden hacer que el código sea más propenso a errores.

A pesar de esto, JavaScript destaca en su capacidad para manejar asincronía con generadores asíncronos, permitiendo trabajar con datos que llegan de manera progresiva, algo que en Kotlin requiere el uso de Flow o coroutines.

En definitiva, la elección entre JavaScript y Kotlin dependerá del contexto y las necesidades del proyecto: si buscas flexibilidad y asincronía, JavaScript es una excelente opción; si priorizas seguridad de tipos y composición fluida de operaciones, Kotlin tiene ventajas significativas.

🔑 Puntos clave

  • Los generadores en JavaScript (function*) permiten crear secuencias perezosas sin consumir más memoria de la necesaria.
  • Los generadores asíncronos (async function*) son útiles para manejar flujos de datos que llegan de forma progresiva.
  • Kotlin ofrece una API más estructurada para secuencias perezosas, permitiendo encadenar operaciones como map y filter sin necesidad de convertirlas en listas.
  • JavaScript es más flexible, pero menos seguro en tiempo de compilación, mientras que Kotlin prioriza la seguridad de tipos.
  • Ambos lenguajes permiten flujos infinitos, pero Kotlin proporciona un control más estricto sobre los tipos de datos.

Los generadores y secuencias perezosas son herramientas fundamentales para escribir código más eficiente y escalable. Elegir la mejor opción dependerá del equilibrio entre flexibilidad, seguridad y facilidad de composición en cada caso. 🚀

Bibliografías Recomendadas