Anwendungsfälle für JavaScript-Generatoren
JavaScript-Generatoren (function*) sind seit ES2015 Teil der Sprache, dennoch greifen viele Frontend-Entwickler immer noch zu Arrays oder Promise-Ketten, obwohl Generatoren die sauberere Lösung wären. Der Kernvorteil liegt nicht in der reinen Geschwindigkeit – es geht um Lazy Evaluation, Komponierbarkeit und präzise Kontrolle über die Iteration. Dieser Artikel behandelt Situationen, in denen Generatoren ihren Platz in modernem Frontend-Code wirklich verdienen.
Wichtigste Erkenntnisse
- Generatoren erzeugen Werte bei Bedarf durch Lazy Evaluation und vermeiden so unnötige Berechnungen und Zwischen-Arrays
- Die Iterator Helpers API bringt eingebaute
map-,filter- undtake-Methoden für von Generatoren zurückgegebene Iteratoren und macht benutzerdefinierte Hilfsfunktionen überflüssig yield*macht die rekursive Traversierung von Bäumen und Graphen sowohl lesbar als auch lazy- Asynchrone Generatoren (
async function*) arbeiten zusammen mitfor await...of, um paginierte oder stapelweise Datenabfragen mit minimalem State-Management zu handhaben
Was JavaScript-Generatoren unterscheidet
Eine Generatorfunktion gibt einen Iterator zurück. Der Aufruf der Funktion führt keinen Code aus – sie übergibt Ihnen ein Objekt mit einer .next()-Methode. Jeder Aufruf von .next() führt den Funktionskörper bis zum nächsten yield aus und pausiert dann, wobei der lokale Zustand über Aufrufe hinweg erhalten bleibt.
function* range(start, end) {
for (let i = start; i < end; i++) yield i
}
for (const n of range(0, 5)) {
console.log(n) // 0, 1, 2, 3, 4
}
Da Generatoren das Iterator-Protokoll implementieren, funktionieren sie direkt mit for...of, Spread-Syntax und Destructuring – ohne Adapter.
Lazy Iteration in JavaScript: Datenverarbeitung ohne Materialisierung
Der Hauptgrund, einen Generator statt eines Arrays zu verwenden, ist Lazy Iteration: Werte werden nur bei Bedarf erzeugt. Das ist wichtig, wenn:
- Der vollständige Datensatz groß ist und Sie nur einen Teil davon benötigen
- Die Berechnung jedes Werts aufwändig ist
- Die Sequenz konzeptionell unendlich ist
function* naturals() {
let n = 0
while (true) yield n++
}
// Berechnet nur Werte bis zum Break-Point
for (const n of naturals()) {
if (n > 100) break
}
Es wird kein Zwischen-Array erstellt. Es werden keine Werte über den Break-Point hinaus berechnet.
Die Iterator Helpers API: Eingebaute Lazy Pipelines
Das Schreiben benutzerdefinierter map-, filter- und take-Hilfsfunktionen war früher notwendiger Boilerplate-Code. Die Iterator Helpers API – jetzt in allen modernen Browsern verfügbar – fügt diese direkt zu synchronen Iteratoren hinzu:
const result = naturals()
.filter(n => n % 2 === 0)
.map(n => n * n)
.take(5)
.toArray() // [0, 4, 16, 36, 64]
Jeder Schritt ist lazy. .toArray() ist das, was die Auswertung auslöst. Dies macht Generator-basierte Pipelines deutlich sauberer ohne Drittanbieter-Bibliotheken. Beachten Sie, dass diese Helfer für synchrone Iteratoren gelten – asynchrone Iterator-Helfer sind noch nicht umgebungsübergreifend standardisiert.
Discover how at OpenReplay.com.
Baum- und Graphen-Traversierung
Generatoren sind eine natürliche Lösung für die Traversierung rekursiver Strukturen. Die Tiefensuche in einem DOM-ähnlichen Baum wird unkompliziert:
function* walkTree(node) {
yield node
for (const child of node.children ?? []) {
yield* walkTree(child)
}
}
for (const node of walkTree(rootNode)) {
if (node.type === 'input') processInput(node)
}
yield* delegiert an einen verschachtelten Generator, hält die Rekursion lesbar und die Traversierung lazy – Sie stoppen, sobald Sie finden, was Sie brauchen.
Asynchrone Generatoren in JavaScript: Paginierte und stapelweise Datenabfrage
async function* erweitert das Muster auf asynchrone Sequenzen. In Kombination mit for await...of eignet es sich gut für paginierte API-Antworten:
async function* fetchPages(url) {
let nextUrl = url
while (nextUrl) {
const res = await fetch(nextUrl)
const data = await res.json()
yield data.items
nextUrl = data.nextPage ?? null
}
}
for await (const batch of fetchPages('/api/records')) {
processBatch(batch)
}
Jede Seite wird nur abgerufen, wenn die Schleife fortschreitet. Es ist nicht nötig, alle Seiten im Voraus zu sammeln oder den Paginierungszustand extern zu verwalten – der Generator hält ihn.
Wann man Generatoren nicht verwenden sollte
Generatoren fügen Indirektion hinzu. Für eine einfache Array-Transformation, die Sie vollständig verbrauchen werden, sind verkettete Array-Methoden klarer. Verwenden Sie Generatoren, wenn die Sequenz groß, potenziell unendlich ist oder wenn Sie früh stoppen müssen, ohne verschwendete Berechnungen.
Fazit
JavaScript-Generatoren glänzen in drei Bereichen: Lazy Iteration über große oder unendliche Sequenzen, komponierbare Daten-Pipelines (besonders mit der Iterator Helpers API) und asynchrone Datenabfrage, bei der Sie sequenzielle, zustandsbehaftete Kontrolle benötigen. Sie sind kein Ersatz für Arrays oder async/await – sie sind das richtige Werkzeug, wenn Sie Werte bei Bedarf erzeugen müssen, anstatt alle auf einmal.
FAQs
Ja. Generatoren funktionieren gut, um Datensequenzen zu erzeugen, die React-Komponenten konsumieren. Sie können einen Generator innerhalb eines useEffect- oder useMemo-Hooks aufrufen, um Werte lazy zu berechnen. Verwenden Sie jedoch keinen Generator als Komponentenfunktion selbst – React erwartet, dass Komponenten JSX zurückgeben, keine Iteratoren.
Der Generator bleibt an seinem letzten yield-Punkt pausiert. Er wird für die Garbage Collection freigegeben, sobald keine Referenzen auf sein Iterator-Objekt mehr bestehen. Wenn Sie Cleanup-Logik ausführen müssen, wenn die Iteration vorzeitig stoppt, umschließen Sie das yield mit einem try-finally-Block. Der finally-Block wird ausgeführt, wenn die return-Methode des Iterators aufgerufen wird oder der Generator vom Garbage Collector erfasst wird.
Bei kleinen, vollständig konsumierten Collections haben Generatoren einen leichten Overhead durch den Pause-und-Resume-Mechanismus. Der Performance-Unterschied ist in den meisten Anwendungen vernachlässigbar. Generatoren werden in der Praxis schneller, wenn Sie große Datensätze teilweise verarbeiten, da sie die Allokation von Zwischen-Arrays vermeiden und die Berechnung von Werten überspringen, die Sie nie anfordern.
Ein asynchroner Generator liefert Werte inkrementell, sobald sie verfügbar werden, während ein Promise-basierter Ansatz wartet, bis alle Daten gesammelt sind, bevor er zurückkehrt. Das bedeutet, dass asynchrone Generatoren es Ihnen ermöglichen, sofort mit der Verarbeitung des ersten Ergebnis-Batches zu beginnen, den Spitzenspeicherverbrauch zu reduzieren und Ihnen feinere Kontrolle darüber zu geben, wann jeder nachfolgende Abruf erfolgt.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.