Start · Sprachen · JavaScript · Referenz · Symbol.asyncIterator

Symbol.asyncIterator

Eigenschaft

Wohlbekanntes Symbol, über das ein Objekt seinen asynchronen Iterator bereitstellt. <code>for await...of</code> verwendet es.

seit JavaScript ES2018 Kategorie: method-property

Signatur

Symbol.asyncIterator

Beschreibung

Symbol.asyncIterator ist eine statische Dateneigenschaft von Symbol. Sie enthält eines der wohlbekannten Symbole (englisch „well-known symbols“). Hat ein Objekt eine Methode unter diesem Schlüssel, gilt es als asynchron iterierbar. Die Methode muss ein Objekt zurückgeben, das das Async-Iterator-Protokoll erfüllt. Dessen next()-Methode liefert also ein Promise, das zu { value, done } aufgelöst wird.

Die Schleife for await...of und yield* in asynchronen Generatoren rufen diese Methode automatisch auf. Array.fromAsync() tut das ebenfalls. Das Symbol ist nützlich, wenn Daten nach und nach eintreffen: zum Beispiel bei seitenweise abgerufenen API-Ergebnissen, Datenströmen (Streams), WebSocket-Nachrichten oder Zeilen aus Dateien.

Am einfachsten implementierst du die Methode als asynchronen Generator (async *[Symbol.asyncIterator]() { … }). Der Generator erzeugt automatisch ein Objekt, das dem Protokoll entspricht. Eingebaut haben das Symbol zum Beispiel ReadableStream (in neueren Browsern und in Node.js) sowie Node.js-Readable-Streams und events.on().

Die Eigenschaft selbst ist nicht beschreibbar (writable: false), nicht aufzählbar (enumerable: false) und nicht konfigurierbar (configurable: false).

Rückgabewert

Typ
symbol
Beschreibung
Das wohlbekannte Symbol Symbol.asyncIterator. Es ist in allen Realms (z. B. iframes) identisch.

Beispiele

Eigenes asynchron iterierbares Objekt mit async-Generator

const countdown = {
  from: 3,
  async *[Symbol.asyncIterator]() {
    for (let i = this.from; i > 0; i--) {
      // Verzögerung simulieren
      await new Promise((r) => setTimeout(r, 100));
      yield i;
    }
  },
};

for await (const n of countdown) {
  console.log(n);
}
3 2 1

Seitenweise API-Abfrage (Pagination) als asynchroner Iterator

class PagedResource {
  constructor(url) {
    this.url = url;
  }

  async *[Symbol.asyncIterator]() {
    let next = this.url;
    while (next) {
      const res = await fetch(next);
      const { items, nextPage } = await res.json();
      yield* items; // einzelne Einträge weiterreichen
      next = nextPage;
    }
  }
}

const users = new PagedResource('https://api.example.com/users?page=1');
for await (const user of users) {
  console.log(user.name);
  if (user.name === 'Stop') break; // break ruft return() auf und beendet den Generator
}

Protokoll manuell implementieren (ohne Generator)

const ticker = {
  [Symbol.asyncIterator]() {
    let count = 0;
    return {
      next: () =>
        Promise.resolve(
          count < 2 ? { value: `Tick ${++count}`, done: false } : { value: undefined, done: true }
        ),
      return: () => {
        console.log('Aufräumen');
        return Promise.resolve({ value: undefined, done: true });
      },
    };
  },
};

const result = await Array.fromAsync(ticker);
console.log(result);
console.log(typeof ticker[Symbol.asyncIterator]);
[ 'Tick 1', 'Tick 2' ] function

ReadableStream mit for await...of lesen

const response = await fetch('https://example.com/daten.txt');
const decoder = new TextDecoder();

for await (const chunk of response.body) {
  console.log('Empfangen:', decoder.decode(chunk, { stream: true }).length, 'Zeichen');
}

// Wichtig · Fallstricke

  • Nicht mit Symbol.iterator verwechseln: Normale Arrays haben kein Symbol.asyncIterator. Trotzdem funktioniert for await...of mit ihnen, weil die Schleife auf Symbol.iterator zurückgreift und jeden Wert mit await auflöst. Umgekehrt klappt das nicht: Ein reines Async-Iterable lässt sich nicht mit for...of oder dem Spread-Operator (...) durchlaufen.
  • for await...of ist nur in async-Funktionen erlaubt oder auf oberster Ebene in ES-Modulen (Top-Level-await).
  • Bei manueller Implementierung muss next() ein Promise zurückgeben. Liefert es direkt ein { value, done }-Objekt, funktioniert das zwar, weil await auch Nicht-Promises annimmt. Es widerspricht aber dem Protokoll. Biete außerdem eine return()-Methode an, damit Ressourcen bei break oder bei Fehlern freigegeben werden.
  • Die Elemente werden nacheinander verarbeitet, nicht parallel. Für echte Parallelität nutze Promise.all().
  • Kompatibilität: Das Symbol wird seit ES2018 in allen modernen Browsern und ab Node.js 10 unterstützt. ReadableStream ist erst in neueren Browsern asynchron iterierbar (Safari erst spät). Prüfe die Unterstützung also gegebenenfalls mit Symbol.asyncIterator in stream.