Ce face generatori cu tipuri
Un generator este o funcție scrisă cu function* care se poate opri la fiecare yield și continua mai târziu. Apelul ei nu rulează codul imediat, ci returnează un obiect pe care îl parcurgi cu for...of, cu ... sau cu next(). Este util pentru șiruri lungi sau infinite, calculate doar când e nevoie.
Tipul este Generator<T, TReturn, TNext>: T este tipul valorilor din yield, TReturn tipul valorii din return (implicit void/any), iar TNext tipul valorii primite prin next(valoare). De obicei ajunge Generator<T> sau poți lăsa tipul să fie dedus. Funcțiile async function* au tipul AsyncGenerator<T> și se parcurg cu for await...of.
Pentru obiecte proprii care se pot parcurge cu for...of vezi iteratori. Opțiunea strictBuiltinIteratorReturn (parte din strict) dă tipul corect, undefined, la valoarea de final a iteratorilor predefiniți.
Exemple
Exemplul 1: Șir calculat la cerere
Generatorul este infinit, dar fiecare valoare se calculează abia când bucla o cere.
function* fibonacci(): Generator<number> {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const primele: number[] = [];
for (const n of fibonacci()) {
if (n > 100) break;
primele.push(n);
}
console.log(primele.join(" "));
Rezultat
0 1 1 2 3 5 8 13 21 34 55 89
Exemplul 2: Toate cele trei tipuri: yield, return și next
function* casa(): Generator<string, number, number> {
let total = 0;
while (true) {
const pret: number = yield `total: ${total} lei`;
if (pret === 0) return total;
total += pret;
}
}
const g = casa();
console.log(g.next(0).value); // primul next pornește generatorul
console.log(g.next(12.5).value);
console.log(g.next(7).value);
const final = g.next(0);
console.log(final.done, final.value);
Rezultat
total: 0 lei
total: 12.5 lei
total: 19.5 lei
true 19.5
Exemplul 3: Generator asincron
async function* pagini(total: number): AsyncGenerator<string[]> {
const elevi = ["Ana", "Mihai", "Ioana", "Andrei", "Maria"];
for (let i = 0; i < elevi.length; i += total) {
await new Promise((r) => setTimeout(r, 10)); // simulează o cerere la server
yield elevi.slice(i, i + total);
}
}
let numar = 1;
for await (const pagina of pagini(2)) {
console.log(`pagina ${numar++}:`, pagina);
}
Rezultat
pagina 1: [ 'Ana', 'Mihai' ]
pagina 2: [ 'Ioana', 'Andrei' ]
pagina 3: [ 'Maria' ]
Greșeli frecvente
- Apelezi generatorul și te aștepți să ruleze:
fibonacci()singur nu face nimic până nu îl parcurgi. - Parcurgi același obiect generator de două ori. După prima parcurgere este epuizat; apelează din nou funcția.
Comenzi înrudite
iteratori cu tipuri: Un obiect cu metoda [Symbol.iterator] se poate parcurge cu for…of; tipurile sunt Iterable<T> și Iterator<T>.async/await cu tipuri: O funcție async returnează mereu Promise<T>; await scoate valoarea de tip T din Promise.Promise<T>: Tipul unei valori care va fi disponibilă mai târziu; T este tipul rezultatului, nu și al erorii.strictBuiltinIteratorReturn: Iteratorii obținuți din Array, Map, Set și String au valoarea finală (done: true) de tip undefined, nu any.