
Iterator helpers: .map(), .filter() e .take() direttamente sugli iteratori
Perché prima serviva Array.from() per usare map/filter su un iteratore, i nuovi metodi lazy per iteratori, e un esempio pratico con un generatore infinito.
Generatori e iteratori fanno parte di JavaScript dall’ES2015, ma per quasi un decennio hanno avuto una lacuna evidente: non potevi fare .map() o .filter() direttamente su uno di essi. Gli iterator helper colmano questa lacuna — e a differenza dei metodi degli array che già conosci, sono lazy.
Il vecchio problema: nessun metodo array sugli iteratori
Un iteratore garantisce solo .next() — nessun .map, nessun .filter, niente di ciò che ti aspetteresti da Array.prototype. La soluzione standard era convertire prima in array:
function* naturalNumbers() {
let n = 1;
while (true) yield n++;
}
// Array.from materializza tutto — impossibile per un generatore infinito
const firstFive = Array.from(naturalNumbers()).slice(0, 5); // si blocca per sempre
Quella chiamata Array.from() prova a svuotare completamente l’iteratore in memoria prima che tu possa farci qualsiasi cosa — il che va bene per una sequenza breve e finita, ma si rompe completamente per qualcosa di infinito o semplicemente molto grande, dato che pagheresti il costo di memoria e tempo per materializzare dati di cui potresti aver bisogno solo di una fetta.
I nuovi metodi
Ogni iteratore (inclusi i generatori) ora li ha integrati:
function* naturalNumbers() {
let n = 1;
while (true) yield n++;
}
const firstFiveEvenSquares = naturalNumbers()
.filter((n) => n % 2 === 0)
.map((n) => n * n)
.take(5)
.toArray();
console.log(firstFiveEvenSquares); // [4, 16, 36, 64, 100]
Nessun Array.from(), nessun rischio di bloccarsi — take(5) smette di estrarre valori dal generatore infinito sottostante nel momento in cui ne ha 5, e filter/map elaborano solo i valori che arrivano davvero fin lì.
Riferimento dei metodi
| Metodo | Cosa fa |
|---|---|
.map(fn) |
Trasforma ogni valore in modo lazy |
.filter(fn) |
Mantiene solo i valori che soddisfano il predicato |
.take(n) |
Si ferma dopo n valori |
.drop(n) |
Salta i primi n valori |
.flatMap(fn) |
Mappa e poi appiattisce di un livello, come il metodo array |
.reduce(fn, initial) |
Riduce a un singolo valore, consumando l’iteratore |
.toArray() |
Materializza i valori rimanenti in un array |
.forEach(fn) |
Esegue una callback per ogni valore, consumando l’iteratore |
.some(fn) / .every(fn) / .find(fn) |
Stessa semantica delle versioni array |
map, filter, take, drop e flatMap restituiscono tutti un nuovo iteratore senza consumare quello originale in modo eager — estraggono valori solo quando servono. reduce, toArray, forEach, some, every e find sono le operazioni “terminali” che effettivamente guidano l’iterazione.
Perché la pigrizia (laziness) conta qui
La differenza chiave rispetto ai metodi di Array.prototype è che i metodi degli array sono eager — [1,2,3].map(fn) elabora ogni elemento immediatamente e alloca un intero nuovo array, anche se finisci per usare solo il primo risultato. Gli iterator helper elaborano un valore alla volta, su richiesta, il che significa:
- Puoi concatenare operazioni su una sequenza infinita, finché qualcosa a valle (
take,find, ecc.) prima o poi smette di estrarre valori. - Gli array intermedi non vengono mai allocati — utile quando elabori un dataset grande dove materializzare ogni step intermedio sprecherebbe memoria.
// Questa riga non fa ancora nessun lavoro — niente è stato estratto dall'iteratore
const pipeline = naturalNumbers()
.filter((n) => n % 3 === 0)
.map((n) => n * n);
// Solo ora l'iterazione parte davvero, e si ferma appena ha 3 valori
console.log(pipeline.take(3).toArray()); // [9, 36, 81]
💡 Consiglio
Se stai elaborando un dataset grande ma finito — leggendo un file grande riga per riga, streamando
risultati di query — gli iterator helper ti permettono di scrivere lo stesso stile
.filter().map() che useresti su un array, senza mai tenere l’intero dataset in memoria in una
volta sola.
FAQ
❓ Funzionano anche sui normali array?
Gli array hanno già i propri map/filter/ecc., che restano eager e invariati — gli iterator
helper si applicano specificamente a iteratori e generatori. Puoi collegare le due direzioni:
array.values() ti dà un iteratore su cui puoi concatenare iterator helper, e .toArray()
converte indietro.
❓ Gli iterator helper funzionano con i generatori async?
Sì — AsyncIterator.prototype ha ricevuto l’equivalente set di helper (map, filter, take, e
così via), usabili allo stesso modo su un generatore async function*, aspettando ogni step
quando serve.
❓ È la stessa cosa degli observable di RxJS?
Concettualmente correlato — entrambi riguardano la composizione di operazioni lazy e concatenabili su una sequenza di valori — ma gli iterator helper sono un sottoinsieme molto più piccolo, sincrono di default, integrato nel linguaggio, non un sostituto del set di feature molto più ampio di RxJS (multicasting, scheduler, centinaia di operatori) negli scenari di reactive programming che ne hanno davvero bisogno.
Conclusione
Gli iterator helper finalmente permettono di fare .map(), .filter() e .take() direttamente su qualsiasi iteratore o generatore, senza prima svuotarlo in un array — cosa che prima era o dispendiosa per sequenze grandi o del tutto impossibile per quelle infinite. Usali ogni volta che elaborare una sequenza in modo lazy ha senso: generatori infiniti, dataset grandi, o qualsiasi pipeline dove ti servono solo i primi risultati.
Riferimenti

Co-Fondatore & CTO presso PAPION. Senior full-stack engineer specializzato in React, TypeScript, Node.js e sicurezza applicativa.