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:
-
Ausführung pausieren (
yield): Wenn der Befehlyielderreicht 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. -
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. -
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 Methodenext()übergeben. Der Generator setzt die Ausführung an der Stelle fort, an der er pausiert wurde. -
Übertragung des Ergebnisses: Der an
next()übergebene Wert wird zum Ergebnis des Ausdrucksyieldinnerhalb 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.