VorwortGeneratorfunktionen gibt es in JavaScript schon seit der Einführung von async/await. Daher muss man beim Erstellen asynchroner Generatoren (Generatoren, die immer ein Promise zurückgeben und auf die gewartet werden kann) viele Dinge beachten. Heute schauen wir uns asynchrone Generatoren und ihren engen Verwandten, die asynchrone Iteration, an. Hinweis: Obwohl diese Konzepte für alle modernen JavaScript-Implementierungen gelten sollten, wurde der gesamte Code in diesem Artikel mit den Node.js-Versionen 10, 12 und 14 entwickelt und getestet. AsynchrongeneratorfunktionenSchauen Sie sich dieses kleine Programm an: // Datei: main.js const createGenerator = Funktion*(){ Ertrag 'a' Ertrag 'b' Ertrag 'c' } const main = () => { const generator = erstelleGenerator() für (const item of generator) { console.log(Element) } } hauptsächlich() Dieser Code definiert eine Generatorfunktion, erstellt ein Generatorobjekt mithilfe der Funktion und iteriert dann mithilfe einer for ... of-Schleife über das Generatorobjekt. Ziemlich normale Sachen – obwohl Sie im wirklichen Leben nie einen Generator für so etwas Triviales verwenden würden. Wenn Sie mit Generatoren und for ... of-Schleifen nicht vertraut sind, lesen Sie bitte die Artikel Javascript-Generatoren und ES6-Schleifen und Iterables. Bevor Sie asynchrone Generatoren verwenden, müssen Sie über solide Kenntnisse zu Generatoren und for...of-Schleifen verfügen. Angenommen, wir möchten in unserer Generatorfunktion „await“ verwenden. Node.js unterstützt diese Funktion, solange wir die Funktion mit dem Schlüsselwort „async“ deklarieren. Wenn Sie mit asynchronen Funktionen nicht vertraut sind, sehen Sie sich den Artikel „Schreiben asynchroner Aufgaben in modernem JavaScript“ an. Lassen Sie uns das Programm ändern und „await“ im Generator verwenden. // Datei: main.js const createGenerator = asynchrone Funktion*(){ yield warte auf neues Promise((r) => r('a')) Ertrag 'b' Ertrag 'c' } const main = () => { const generator = erstelleGenerator() für (const item of generator) { console.log(Element) } } hauptsächlich() Im wirklichen Leben würden Sie dies auch nicht tun – Sie würden wahrscheinlich auf eine Funktion einer API oder Bibliothek eines Drittanbieters warten. Um das Verständnis für jeden zu erleichtern, sind unsere Beispiele möglichst einfach gehalten. Wenn Sie versuchen, das obige Programm auszuführen, tritt das folgende Problem auf: $ Knoten main.js /Benutzer/alanstorm/Desktop/main.js:9 für (const item of generator) { ^ TypeError: Generator ist nicht iterierbar JavaScript teilt uns mit, dass dieser Generator „nicht iterierbar“ ist. Auf den ersten Blick könnte es so aussehen, als ob die asynchrone Funktion eines Generators auch bedeutet, dass der von ihm erzeugte Generator nicht iterierbar ist. Dies ist etwas verwirrend, da der Zweck eines Generators darin besteht, Objekte zu erzeugen, die „programmgesteuert“ iterierbar sind. Finden Sie als Nächstes heraus, was passiert ist. Überprüfen Sie den GeneratorWenn man sich die iterierbaren Objekte von JavaScript-Generatoren ansieht [1]. Ein Objekt implementiert das Iteratorprotokoll, wenn es über eine nächste Methode verfügt und die nächste Methode ein Objekt mit einer Werteigenschaft, einer Fertigeigenschaft oder sowohl einer Werteigenschaft als auch einer Fertigeigenschaft zurückgibt. Vergleichen wir die von einer asynchronen Generatorfunktion zurückgegebenen Generatorobjekte mit denen, die von einer regulären Generatorfunktion zurückgegeben werden, indem wir den folgenden Code verwenden: // Datei: test-program.js const createGenerator = Funktion*(){ Ergebnis 'a' Ertrag 'b' Ertrag 'c' } const createAsyncGenerator = asynchrone Funktion*(){ yield warte auf neues Promise((r) => r('a')) Ertrag 'b' Ertrag 'c' } const main = () => { const generator = erstelleGenerator() const asyncGenerator = erstelleAsyncGenerator() Konsole.log('Generator:',Generator[Symbol.Iterator]) Konsole.log('asyncGenerator',asyncGenerator[Symbol.iterator]) } hauptsächlich() Sie werden sehen, dass Ersteres keine Symbol.iterator-Methode hat, Letzteres jedoch schon. $ Knoten Testprogramm.js Generator: [Funktion: [Symbol.Iterator]] asyncGenerator undefiniert Beide Generatorobjekte haben eine Next-Methode. Wenn Sie den Testcode ändern, um diese nächste Methode aufzurufen: // Datei: test-program.js /* … */ const main = () => { const generator = erstelleGenerator() const asyncGenerator = erstelleAsyncGenerator() Konsole.log('Generator:',Generator.Weiter()) Konsole.log('asyncGenerator',asyncGenerator.next()) } hauptsächlich() Sie werden auf ein weiteres Problem stoßen: $ Knoten Testprogramm.js Generator: {Wert: 'a', fertig: false} asyncGenerator-Versprechen { <ausstehend> } Damit ein Objekt iterierbar ist, muss die nächste Methode ein Objekt mit den Eigenschaften „Wert“ und „Fertig“ zurückgeben. Eine asynchrone Funktion gibt immer ein Promise-Objekt zurück. Diese Funktion gilt für Generatoren, die mit asynchronen Funktionen erstellt wurden – diese asynchronen Generatoren ergeben immer ein Promise-Objekt. Dieses Verhalten macht es asynchronen Generatoren unmöglich, das JavaScript-Iterationsprotokoll zu implementieren. Asynchrone IterationGlücklicherweise gibt es eine Möglichkeit, diesen Widerspruch aufzulösen. Wenn Sie sich den Konstruktor oder die Klasse ansehen, die von einem asynchronen Generator zurückgegeben wird // Datei: test-program.js /* … */ const main = () => { const generator = erstelleGenerator() const asyncGenerator = erstelleAsyncGenerator() Konsole.log('asyncGenerator',asyncGenerator) } Sie können sehen, dass es sich um ein Objekt handelt, dessen Typ oder Klasse oder Konstruktor AsyncGenerator statt Generator ist: asyncGenerator-Objekt [AsyncGenerator] {} Obwohl dieses Objekt möglicherweise nicht iterierbar ist, ist es asynchron iterierbar. Damit ein Objekt asynchron iterierbar ist, muss es eine Symbol.asyncIterator-Methode implementieren. Diese Methode muss ein Objekt zurückgeben, das die asynchrone Version des Iteratorprotokolls implementiert. Das heißt, das Objekt muss über eine Next-Methode verfügen, die ein Promise zurückgibt, und dieses Promise muss letztendlich in ein Objekt mit den Eigenschaften „Done“ und „Value“ aufgelöst werden. Ein AsyncGenerator-Objekt erfüllt alle diese Bedingungen. Bleibt die Frage: Wie können wir über ein Objekt iterieren, das nicht iterierbar ist, aber asynchron iteriert werden kann? für warten … der SchleifeEin asynchrones Iterable kann manuell iteriert werden, indem nur die nächste Methode des Generators verwendet wird. (Beachten Sie, dass die Hauptfunktion hier jetzt async main ist – dies ermöglicht uns, await innerhalb der Funktion zu verwenden) // Datei: main.js const createAsyncGenerator = asynchrone Funktion*(){ yield warte auf neues Promise((r) => r('a')) Ertrag 'b' Ertrag 'c' } const main = async () => { const asyncGenerator = erstelleAsyncGenerator() let Ergebnis = { fertig:false } während(!Ergebnis.fertig) { Ergebnis = warte auf asyncGenerator.next() wenn(Ergebnis.fertig) { weiter; } console.log(Ergebnis.Wert) } } hauptsächlich() Dies ist jedoch nicht der einfachste Looping-Mechanismus. Mir gefällt die While-Schleifenbedingung nicht und ich möchte result.done auch nicht manuell überprüfen. Darüber hinaus muss die Variable result.done im Gültigkeitsbereich sowohl des inneren als auch des äußeren Blocks vorhanden sein. Glücklicherweise unterstützen die meisten (vielleicht alle?) JavaScript-Implementierungen, die asynchrone Iteratoren unterstützen, auch die spezielle for await ...-Schleifensyntax. Zum Beispiel: const createAsyncGenerator = asynchrone Funktion*(){ yield warte auf neues Promise((r) => r('a')) Ertrag 'b' Ertrag 'c' } const main = async () => { const asyncGenerator = erstelleAsyncGenerator() für warte(const item of asyncGenerator) { console.log(Element) } } hauptsächlich() Wenn Sie den obigen Code ausführen, werden Sie sehen, dass der asynchrone Generator und das iterierbare Objekt erfolgreich durchlaufen und der vollständig aufgelöste Wert des Promise im Schleifenkörper abgerufen wird.
Die for await ... of-Schleife bevorzugt Objekte, die das asynchrone Iteratorprotokoll implementieren. Sie können es jedoch verwenden, um über jede Art von iterierbarem Objekt zu iterieren. für warte(const item of [1,2,3]) { console.log(Element) } Wenn Sie „for await“ verwenden, sucht Node.js zuerst nach der Methode Symbol.asyncIterator im Objekt. Wenn keines gefunden werden kann, wird auf die Methode Symbol.iterator zurückgegriffen. Nichtlineare CodeausführungWie await führt die for await-Schleife eine nichtlineare Codeausführung in Ihr Programm ein. Das heißt, Ihr Code wird in einer anderen Reihenfolge ausgeführt, als er geschrieben wurde. Wenn Ihr Programm zum ersten Mal auf eine For-Await-Schleife stößt, ruft es „Next“ für Ihr Objekt auf. Das Objekt gibt ein Versprechen ab, dann verlässt die Ausführung des Codes Ihre asynchrone Funktion und Ihre Programmausführung wird außerhalb dieser Funktion fortgesetzt. Sobald Ihr Versprechen eingelöst ist, kehrt die Codeausführung mit diesem Wert zum Schleifenkörper zurück. Wenn die Schleife endet und es Zeit ist, mit der nächsten Reise fortzufahren, ruft Node.js „next“ für das Objekt auf. Dieser Aufruf erzeugt ein weiteres Versprechen und die Codeausführung verlässt Ihre Funktion erneut. Dieses Muster wiederholt sich, bis das Promise in ein Objekt aufgelöst wird, bei dem „done“ wahr ist. Anschließend wird die Ausführung mit dem Code nach der For-Await-Schleife fortgesetzt. Das folgende Beispiel veranschaulicht diesen Punkt: lass count = 0 const getCount = () => { zählen++ gibt `${count}` zurück. } const createAsyncGenerator = asynchrone Funktion*() { console.log(getCount() + 'createAsyncGenerator wird aufgerufen') console.log(getCount() + 'im Begriff, ein Ergebnis zu liefern') Ertrag, warte auf neues Versprechen((r)=>r('a')) console.log(getCount() + 'createAsyncGenerator erneut aufrufen') console.log(getCount() + 'im Begriff, b zu ergeben') Ertrag 'b' console.log(getCount() + 'createAsyncGenerator erneut aufrufen') console.log(getCount() + 'im Begriff, c zu ergeben') Ertrag 'c' console.log(getCount() + 'createAsyncGenerator erneut aufrufen') console.log(getCount() + 'createAsyncGenerator beenden') } const main = async () => { console.log(getCount() + 'Hauptkonto wird aufgerufen') const asyncGenerator = erstelleAsyncGenerator() console.log(getCount() + 'Starte Warteschleife') für warte(const item of asyncGenerator) { console.log(getCount() + 'Eintreten in Warteschleife') Konsole.log(getCount() + Element) console.log(getCount() + 'Beenden für Warteschleife') } console.log(getCount() + 'fertig mit for-Await-Schleife') console.log(getCount() + 'Hauptverzeichnis verlassen') } console.log(getCount() + 'vor dem Aufruf von main') hauptsächlich() console.log(getCount() + 'nach dem Aufruf von main') In diesem Code verwenden Sie nummerierte Protokollierungsanweisungen, mit denen Sie seine Ausführung verfolgen können. Zur Übung sollten Sie das Programm selbst ausführen und die Ergebnisse prüfen. Asynchrone Iteration ist eine leistungsstarke Technik, die bei der Ausführung Ihres Programms zu Verwirrung führen kann, wenn Sie nicht wissen, wie sie funktioniert. ZusammenfassenDies ist das Ende dieses Artikels über asynchrone Generatoren und asynchrone Iterationen in Node.js. Weitere relevante Inhalte zu asynchronen Generatoren und asynchronen Iterationen in Node.js finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen! Das könnte Sie auch interessieren:
|
<<: 8 Gründe, warum Sie die Xfce-Desktopumgebung für Linux verwenden sollten
>>: Der Unterschied und die Verwendung von distinct und row_number() over() in SQL
● Ich hatte vor, einige Cloud-Daten zu kaufen, um...
Heutzutage beginnen viele Leute damit, Websites z...
Vorwort: Manchmal ist der Hauptteil einer Route d...
Die Installation des RPM-Pakets ist relativ einfa...
Hintergrund Folgendes ist passiert: Luzhu erfuhr ...
Methode 1: Verwenden Sie den Befehl SET PASSWORD ...
<br />Ich habe mir heute die neu gestaltete ...
Installieren Sie die entpackte Version von Mysql ...
Die MySQL-ID beginnt bei 1 und erhöht sich automa...
Vorwort Ich muss dem Markodwn-Editor, den ich ger...
Definition von Float Setzt das Element aus dem no...
1. Schauen wir uns zunächst eine Anweisung zur Ta...
Rendern Nachdem ich online nach relevanten Inform...
Heute muss das Unternehmensprojekt Docker konfigu...
Heute habe ich eine virtuelle Maschine für ein Ex...