Generatori JavaScript

I generatori in JavaScript sono funzioni speciali che possono essere messe in pausa e riprese durante la loro esecuzione. A differenza delle funzioni normali che eseguono tutto il codice dall’inizio alla fine, i generatori permettono di fermarsi in punti specifici e continuare successivamente, mantenendo il loro stato interno.
Come Funzionano i Generatori
Un generatore si riconosce dalla sintassi function* (con l’asterisco) e utilizza la parola chiave yield per mettere in pausa l’esecuzione. Quando chiamiamo un generatore, non esegue immediatamente il codice, ma restituisce un oggetto iteratore che possiamo controllare manualmente.
function* contatoreBase() {
yield 1;
yield 2;
yield 3;
}
const gen = contatoreBase();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
Ogni chiamata a next() fa riprendere l’esecuzione fino al prossimo yield, restituendo un oggetto con il valore corrente e lo stato di completamento.
La Parola Chiave yield
yield è il cuore dei generatori. Quando il generatore incontra yield, si ferma e restituisce il valore specificato. La prossima volta che chiamiamo next(), l’esecuzione riprende dalla riga successiva al yield.
function* esempioyield() {
console.log("Inizio");
yield "primo valore";
console.log("Tra primo e secondo");
yield "secondo valore";
console.log("Fine");
return "terminato";
}
Il generatore può anche ricevere valori dall’esterno tramite il parametro di next():
function* comunicazione() {
const messaggio = yield "Dimmi qualcosa";
yield `Hai detto: ${messaggio}`;
}
const gen = comunicazione();
console.log(gen.next()); // { value: "Dimmi qualcosa", done: false }
console.log(gen.next("Ciao")); // { value: "Hai detto: Ciao", done: false }
Generatori Infiniti
Una delle caratteristiche più potenti dei generatori è la capacità di creare sequenze infinite senza problemi di memoria, perché i valori vengono calcolati solo quando richiesti:
function* numeriInfiniti() {
let n = 0;
while (true) {
yield n++;
}
}
const numeri = numeriInfiniti();
console.log(numeri.next().value); // 0
console.log(numeri.next().value); // 1
console.log(numeri.next().value); // 2
// Può continuare all'infinito
Utilizzi Pratici
Sequenze Matematiche
I generatori sono perfetti per implementare sequenze matematiche come Fibonacci:
function* fibonacci() {
let a = 0,
b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
// Prendi i primi 10 numeri di Fibonacci
const fib = fibonacci();
const primi10 = [];
for (let i = 0; i < 10; i++) {
primi10.push(fib.next().value);
}
console.log(primi10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
ID Univoci
Creazione di identificatori univoci progressivi:
function* generatoreID() {
let id = 1;
while (true) {
yield `user_${id++}`;
}
}
const idGen = generatoreID();
const user1 = idGen.next().value; // "user_1"
const user2 = idGen.next().value; // "user_2"
Processamento Step-by-Step
I generatori sono utili per processare operazioni complesse un passo alla volta:
function* elaborazioneDati(dati) {
yield "Validazione in corso...";
// Simula validazione
yield "Trasformazione in corso...";
// Simula trasformazione
yield "Salvataggio in corso...";
// Simula salvataggio
return "Elaborazione completata";
}
Integrazione con for…of
I generatori sono automaticamente iterabili, quindi funzionano perfettamente con i cicli for...of:
function* colori() {
yield "rosso";
yield "verde";
yield "blu";
}
for (const colore of colori()) {
console.log(colore); // rosso, verde, blu
}
// Anche convertibili in array
const arrayColori = [...colori()]; // ["rosso", "verde", "blu"]
Vantaggi Principali
Lazy Evaluation: I valori vengono calcolati solo quando necessari, risparmiando memoria e CPU per sequenze grandi o infinite.
Controllo del Flusso: Possibilità di mettere in pausa e riprendere l’esecuzione, utile per operazioni asincrone o step-by-step.
Stato Mantenuto: Il generatore ricorda dove si è fermato e tutte le variabili locali tra una chiamata e l’altra.
Sintassi Pulita: Più leggibile rispetto alla creazione manuale di iteratori personalizzati.
Limitazioni
I generatori non sono adatti per tutte le situazioni. Sono più utili quando hai bisogno di sequenze, iterazioni personalizzate o controllo fine del flusso di esecuzione. Per operazioni semplici o quando hai bisogno di tutti i valori contemporaneamente, gli array tradizionali potrebbero essere più appropriati.
I generatori rappresentano uno strumento potente per scrivere codice più elegante e efficiente quando si lavora con sequenze di dati o processi che possono essere suddivisi in step.
