Iterables e iteradores en JavaScript | Nicolás Garzón
Un iterable es un valor que sabe crear un recorrido. Un iterador es el objeto que entrega los valores de ese recorrido uno a uno.
JavaScript
Copiar const values = [ "A" , "B" , "C" ] ;
const iterator = values[ Symbol. iterator] ( ) ;
iterator. next ( ) ;
JavaScript
Copiar iterator. next ( ) ;
JavaScript
Copiar iterator. next ( ) ;
iterator. next ( ) ;
Tiene un método bajo la clave Symbol.iterator.
JavaScript
Copiar const iterable = {
[ Symbol. iterator] ( ) {
return iterator;
} ,
} ; Su responsabilidad es crear o entregar un iterador.
JavaScript
Copiar const iterator = {
next ( ) {
return {
value : "A" ,
done : false ,
} ;
} ,
} ; Su responsabilidad es recordar el progreso y producir registros de iteración.
Cada llamada a next() retorna un objeto.
JavaScript
Copiar {
value : 10 ,
done : false ,
}
value: valor producido.
done: indica si la secuencia terminó.
JavaScript
Copiar {
value : undefined ,
done : true ,
} Un iterador puede incluir un valor final junto con done: true, aunque for...of normalmente no expone ese valor final.
Muchos iteradores estándar implementan:
JavaScript
Copiar iterator[ Symbol. iterator] ( ) === iterator; Eso permite utilizarlos directamente en for...of.
JavaScript
Copiar const iterator = values. values ( ) ;
for ( const value of iterator) {
console. log ( value) ;
} Este patrón se llama iterable iterator .
Varias partes del lenguaje solicitan Symbol.iterator.
JavaScript
Copiar for ( const value of iterable) {
console. log ( value) ;
} JavaScript
Copiar const values = [ ... iterable] ; JavaScript
Copiar const [ first, second] = iterable; JavaScript
Copiar const values = Array. from ( iterable) ; JavaScript
Copiar new Set ( iterable) ;
new Map ( iterableOfPairs) ; El Map necesita que cada valor producido pueda interpretarse como un par [key, value].
Son iterables, entre otros:
Arrays.
Strings.
Maps.
Sets.
TypedArrays.
Objetos generator.
Algunas colecciones del entorno, como NodeList en navegadores modernos.
Un objeto normal no es iterable automáticamente.
JavaScript
Copiar const user = {
name : "Nicolás" ,
role : "developer" ,
} ;
Puedes recorrer sus propiedades mediante Object.entries.
JavaScript
Copiar for ( const [ key, value] of Object. entries ( user) ) {
console. log ( key, value) ;
} JavaScript
Copiar const arrayLike = {
0 : "A" ,
1 : "B" ,
length : 2 ,
} ; Tiene índices y length, pero no Symbol.iterator.
JavaScript
Copiar
Array. from ( arrayLike) ; Array.from acepta iterables y también objetos array-like; spread exige un iterable.
JavaScript
Copiar function createRange ( start, end ) {
return {
[ Symbol. iterator] ( ) {
let current = start;
return {
next ( ) {
if ( current > end) {
return {
value : undefined ,
done : true ,
} ;
}
return {
value : current++ ,
done : false ,
} ;
} ,
} ;
} ,
} ;
} JavaScript
Copiar for ( const number of createRange ( 1 , 3 ) ) {
console. log ( number) ;
}
Cada llamada a Symbol.iterator crea un recorrido independiente.
JavaScript
Copiar const range = createRange ( 1 , 3 ) ;
[ ... range] ;
[ ... range] ; Un iterable suele poder crear otro iterador.
JavaScript
Copiar const values = [ 1 , 2 , 3 ] ;
[ ... values] ;
[ ... values] ; Un iterador concreto mantiene progreso y normalmente se consume una sola vez.
JavaScript
Copiar const iterator = values. values ( ) ;
[ ... iterator] ;
[ ... iterator] ; No se reinició; ya terminó.
JavaScript
Copiar const iterator = values. values ( ) ; El iterador necesita recordar qué posición sigue. Ese estado no tiene que estar expuesto como una propiedad pública.
Cada iterador nuevo puede mantener un progreso independiente.
JavaScript
Copiar const first = values. values ( ) ;
const second = values. values ( ) ;
first. next ( ) ;
first. next ( ) ;
second. next ( ) ; Un iterador puede implementar return() para limpiar recursos cuando el consumo termina antes de tiempo.
JavaScript
Copiar const iterable = {
[ Symbol. iterator] ( ) {
let current = 0 ;
return {
next ( ) {
return {
value : current++ ,
done : false ,
} ;
} ,
return ( ) {
console. log ( "Closed" ) ;
return {
value : undefined ,
done : true ,
} ;
} ,
} ;
} ,
} ; JavaScript
Copiar for ( const value of iterable) {
if ( value === 2 ) {
break ;
}
}
for...of solicita el cierre del iterador ante ciertas salidas abruptas.
Esto es útil para liberar lectores, archivos, conexiones o estados temporales controlados por una abstracción.
Un iterador puede lanzar un error desde next.
JavaScript
Copiar next ( ) {
throw new Error ( "Cannot read next item" ) ;
} El consumidor recibe ese error.
No utilices { done: true } para ocultar un fallo que debería ser visible; finalización normal y error son contratos diferentes.
Un iterador produce valores cuando se solicitan.
JavaScript
Copiar const iterator = createLargeSequence ( ) ;
iterator. next ( ) ; No necesita construir obligatoriamente todo un array antes de comenzar.
Secuencias grandes.
Pipelines.
Lectura gradual.
Valores potencialmente infinitos.
Sin embargo, perezoso no significa asíncrono ni garantiza que el hilo no se bloquee. Un consumidor síncrono que solicite millones de valores seguirá ejecutando trabajo síncrono.
El estándar moderno ofrece un objeto global Iterator y helpers perezosos para transformar iteradores.
JavaScript
Copiar const result = Iterator. from ( [ 1 , 2 , 3 , 4 ] )
. filter ( ( value ) => value % 2 === 0 )
. map ( ( value ) => value * 10 )
. toArray ( ) ;
Entre los helpers se encuentran:
map.
filter.
flatMap.
take.
drop.
reduce.
toArray.
forEach.
some.
every.
find.
Estas operaciones consumen o producen iteradores y evitan arrays intermedios en transformaciones perezosas.
JavaScript
Copiar const iterator = Iterator. from ( iterable) ; Normaliza un iterable o iterador dentro del sistema de helpers.
JavaScript
Copiar const firstThree = Iterator. from ( numbers)
. take ( 3 )
. toArray ( ) ; ECMAScript 2026 incorpora una operación estática para concatenar iterables de forma perezosa.
JavaScript
Copiar const combined = Iterator. concat (
[ 1 , 2 ] ,
new Set ( [ 3 , 4 ] ) ,
createRange ( 5 , 6 ) ,
) ;
combined. toArray ( ) ;
No necesita construir previamente un gran array combinado.
Los helpers de iteradores son APIs recientes. Comprueba soporte en el runtime objetivo o utiliza una alternativa compatible cuando sea necesario.
Guarda todos sus elementos.
Permite acceso por índice.
Tiene length.
Puede recorrerse varias veces.
Produce valores progresivamente.
No necesita acceso aleatorio.
Puede no conocer la cantidad total.
Normalmente se consume una vez.
Puede representar una secuencia infinita.
Elige según el contrato, no solo por ahorrar memoria.
JavaScript
Copiar function mapIterable ( iterable, transform ) {
return {
* [ Symbol. iterator] ( ) {
for ( const value of iterable) {
yield transform ( value) ;
}
} ,
} ;
} JavaScript
Copiar const doubled = mapIterable (
createRange ( 1 , 3 ) ,
( value ) => value * 2 ,
) ;
[ ... doubled] ; Los helpers estándar reducen la necesidad de construir estas utilidades manualmente, pero entender el protocolo permite reconocer qué hacen.
Un iterable asíncrono utiliza Symbol.asyncIterator y su next produce promesas.
JavaScript
Copiar const iterator = source[ Symbol. asyncIterator] ( ) ;
const result = await iterator. next ( ) ; Se consume con for await...of.
JavaScript
Copiar for await ( const value of source) {
console. log ( value) ;
} Se profundizará en el bloque de asincronía. No es lo mismo que un iterable síncrono que simplemente contiene promesas.
JavaScript
Copiar function createPageSequence ( fetchPage ) {
return {
async * [ Symbol. asyncIterator] ( ) {
let page = 1 ;
while ( true ) {
const result = await fetchPage ( page) ;
if ( result. items. length === 0 ) {
return ;
}
yield result. items;
page++ ;
}
} ,
} ;
} El consumidor puede solicitar páginas gradualmente sin conocer de antemano la cantidad total.
El detalle completo de async generators se estudiará después.
Usar iterable e iterador como sinónimos.
Esperar que un objeto array-like funcione con spread.
Consumir un iterador y esperar reutilizarlo desde el inicio.
Creer que toda iteración perezosa es asíncrona.
Crear una secuencia infinita y expandirla con spread.
Olvidar limpiar recursos al terminar antes de tiempo.
Retornar registros sin una propiedad done coherente.
Depender de helpers recientes sin revisar compatibilidad.
Convertir siempre a array aunque no se necesite acceso por índice.
Un iterable implementa Symbol.iterator.
Un iterador implementa next y produce { value, done }.
Muchos iteradores también son iterables.
for...of, spread y destructuring consumen iterables.
Un iterable puede crear recorridos nuevos; un iterador mantiene progreso.
return permite cierre anticipado.
La evaluación perezosa produce valores bajo demanda, pero sigue pudiendo ser síncrona.
Los helpers de Iterator permiten pipelines perezosos.
Los iterables asíncronos utilizan otro protocolo.
¿Por qué el segundo spread devuelve un array vacío?
JavaScript
Copiar const iterator = [ 1 , 2 , 3 ] . values ( ) ;
[ ... iterator] ;
[ ... iterator] ; Respuesta Porque el primer spread consumió el iterador hasta done: true. El objeto conserva su progreso y no se reinicia. Debe crearse otro iterador desde el iterable original.
Generadores ofrecen una sintaxis más directa para crear iteradores pausables y mantener su estado entre cada valor producido.