Sobes.tech
Middle+

Warum erlauben gewöhnliche Generatoren das Schreiben von asynchronem Code?

sobes.tech KI

Antwort von AI

Die üblichen Generatoren in JavaScript ermöglichen es, asynchronen Code zu schreiben, dank ihrer Fähigkeit, die Ausführung anzuhalten und wieder aufzunehmen, sowie Werte in beide Richtungen zu übertragen (über yield und next()).

So funktioniert es im Kontext der Asynchronität:

  1. Ausführung pausieren (yield): Wenn der Befehl yield erreicht wird, pausiert der Generator und gibt einen Wert zurück. In asynchronem Code kann dieser Wert eine Promise oder eine andere "Aktion" sein, die asynchron ausgeführt werden muss.

  2. Warten auf das asynchrone Ergebnis: Der externe Code (z.B. ein "Runner" oder ein Wrapper für den Generator) erhält den durch yield übergebenen Wert. Wenn es eine Promise ist, wartet er auf deren Auflösung oder Ablehnung.

  3. Fortsetzen der Ausführung (next()): Nach Abschluss der asynchronen Operation (Auflösung der Promise) wird das asynchrone Ergebnis (der Wert der Promise) wieder an den Generator durch die Methode next() übergeben. Der Generator setzt die Ausführung an der Stelle fort, an der er pausiert wurde.

  4. Übertragung des Ergebnisses: Der an next() übergebene Wert wird zum Ergebnis des Ausdrucks yield innerhalb des Generators. So kann der Generator das Ergebnis der asynchronen Operation "erhalten" und damit weiterarbeiten, als ob der Code synchron wäre.

Dieses Muster schafft eine Illusion synchronen Ablaufs für asynchrone Operationen, was den Code lesbarer und handhabbarer macht im Vergleich zur traditionellen Verwendung von Callbacks oder komplexen .then()-Ketten.

Ein Beispiel dafür sind Wrapper wie co (obwohl heute async/await üblicher sind, die im Wesentlichen syntaktischer Zucker über Generatoren und Promises sind):

// Beispiel für die Verwendung eines Generators für Asynchronität
function* myAsyncGenerator() {
  try {
    const result1 = yield Promise.resolve(1); // Pausieren, Promise zurückgeben
    console.log('Erstes Ergebnis:', result1); // Fortsetzen mit Wert

    const result2 = yield new Promise(resolve => setTimeout(() => resolve(2), 500)); // Weitere asynchrone Operation
    console.log('Zweites Ergebnis:', result2);

    return 'Fertig'; // Generator beenden
  } catch (error) {
    console.error('Ein Fehler ist aufgetreten:', error);
  }
}

// Einfacher "Runner" für den Generator (vereinfachte Version)
function run(generatorFunc) {
  const generator = generatorFunc();

  function handleNext(newValue) {
    const result = generator.next(newValue);

    if (!result.done) {
      if (result.value instanceof Promise) {
        result.value.then(handleNext).catch(handleError);
      } else {
        handleNext(result.value);
      }
    } else {
      console.log('Generator beendet mit Ergebnis:', result.value);
    }
  }

  function handleError(error) {
      generator.throw(error);
  }

  handleNext(); // Generator starten
}

// Ausführen unseres asynchronen Generators
run(myAsyncGenerator);

In diesem Beispiel pausiert yield Promise.resolve(1) den Generator. Der "Runner" (oder co) wartet, bis die Promise aufgelöst wird, und setzt dann den Generator mit next(1) fort. Dieser Wert 1 wird zum Ergebnis von yield Promise.resolve(1) im Generator, was es ermöglicht, es in der nächsten Zeile (console.log) zu verarbeiten. Dieser Mechanismus erlaubt es, einen synchronen Ablauf für asynchrone Operationen zu simulieren.