Kurz: Was for…of durchlaufen kann, nennt man iterierbar: Arrays, Strings, Maps, Sets und mehr. Du kannst auch eigene iterierbare Objekte bauen, am einfachsten mit Generatoren.
Teil des Kurses JavaScript
Kapitel 24 von 40 im Kurs JavaScript. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.
Was for...of durchlaufen kann, nennt man iterierbar: Arrays, Strings, Maps, Sets und mehr. Du kannst auch eigene iterierbare Objekte bauen, am einfachsten mit Generatoren.
Das Iterator-Protokoll
Ein Iterator ist ein Objekt mit einer Methode next(), die { value, done } liefert:
const it = [10, 20][Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());Ausgabe:
{ value: 10, done: false }
{ value: 20, done: false }
{ value: undefined, done: true }for...of, Spread (...), Destructuring und Array.from nutzen dieses Protokoll.
Generatoren: function*
Eine Generatorfunktion (function*) liefert Werte mit yield nacheinander und pausiert dazwischen:
function* zaehle(bis) {
for (let i = 1; i <= bis; i++) {
yield i;
}
}
console.log([...zaehle(5)]);
for (const n of zaehle(3)) console.log("Wert", n);
const gen = zaehle(2);
console.log(gen.next(), gen.next(), gen.next());Ausgabe:
[ 1, 2, 3, 4, 5 ]
Wert 1
Wert 2
Wert 3
{ value: 1, done: false } { value: 2, done: false } { value: undefined, done: true }Der Code läuft erst weiter, wenn der nächste Wert angefordert wird (Lazy Evaluation). Dadurch sind auch unendliche Folgen möglich:
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
function* nimm(n, quelle) {
let i = 0;
for (const x of quelle) {
if (i++ >= n) return;
yield x;
}
}
console.log([...nimm(10, fibonacci())]);Ausgabe:
[
0, 1, 1, 2, 3,
5, 8, 13, 21, 34
]Eigene iterierbare Klassen
class Bereich {
constructor(von, bis) { this.von = von; this.bis = bis; }
*[Symbol.iterator]() {
for (let i = this.von; i <= this.bis; i++) yield i;
}
}
const b = new Bereich(3, 7);
console.log([...b], Math.max(...b));
for (const x of b) if (x % 2) console.log(x);Ausgabe:
[ 3, 4, 5, 6, 7 ] 7
3
5
7yield* und Werte in den Generator senden
function* inner() { yield "a"; yield "b"; }
function* outer() { yield 1; yield* inner(); yield 2; }
console.log([...outer()]);
function* dialog() {
const name = yield "Wie heißt du?";
const alter = yield `Hallo ${name}, wie alt bist du?`;
return `${name} ist ${alter}`;
}
const d = dialog();
console.log(d.next().value);
console.log(d.next("Mia").value);
console.log(d.next(17));Ausgabe:
[ 1, 'a', 'b', 2 ]
Wie heißt du?
Hallo Mia, wie alt bist du?
{ value: 'Mia ist 17', done: true }Nützliche Muster
function* mapGen(quelle, f) { for (const x of quelle) yield f(x); }
function* filterGen(quelle, f) { for (const x of quelle) if (f(x)) yield x; }
function* zahlen() { let n = 1; while (true) yield n++; }
const ergebnis = [];
for (const x of filterGen(mapGen(zahlen(), n => n * n), n => n % 2)) {
ergebnis.push(x);
if (ergebnis.length === 5) break;
}
console.log(ergebnis);
console.log(Iterator.from(zahlen()).map(n => n * 2).take(3).toArray());Ausgabe:
[ 1, 9, 25, 49, 81 ]
[ 2, 4, 6 ]Asynchrone Iteratoren
Mit async function* und for await...of verarbeitest du Daten, die nach und nach eintreffen, etwa gelesene Dateistücke oder Seiten einer API:
async function* seiten() {
for (let i = 1; i <= 3; i++) {
await new Promise(r => setTimeout(r, 10));
yield `Seite ${i}`;
}
}
(async () => {
for await (const s of seiten()) console.log(s);
})();Ausgabe:
Seite 1
Seite 2
Seite 3Merke
- Iterierbare Objekte haben
[Symbol.iterator](), Iteratorennext() function*undyielderzeugen Werte nacheinander und pausieren dazwischen- Generatoren sind lazy und können unendlich sein
yield*delegiert an einen anderen Iterierbarenasync function*undfor await...offür Datenströme
Übungsaufgabe
Schreibe einen Generator primzahlen(), der unendlich viele Primzahlen liefert, und gib die ersten zehn aus.
Quiz zur Selbstkontrolle
Was liefert next() eines Iterators?
- { value, done } (richtig)
- Nur den Wert
- true oder false
- Ein Array
Was passiert bei yield?
- Der Generator liefert einen Wert und pausiert (richtig)
- Der Generator endet
- Ein Fehler wird ausgelöst
- Die Funktion startet neu
Was erlaubt for await...of?
- Über asynchrone Datenströme zu iterieren (richtig)
- Mehrere Schleifen gleichzeitig
- Schleifen zu beschleunigen
- Fehler zu ignorieren
Weiter im Kurs
Zurück: Module: import und export
Weiter: Asynchron: Callbacks und Promises
Alle Kapitel: JavaScript im Überblick