1. EinleitungVor ein paar Tagen bin ich bei der Verwendung zur Durchquerung in einem Projekt auf eine Falle gestoßen und habe einen Tag gebraucht, um sie zu beheben. Merken Sie es sich hier einfach. 2. ProblemLassen Sie mich zunächst ein sehr einfaches Thema vorstellen: Drucken Sie ein Array alle 1 Sekunde aus. Hier füge ich den Code ein, den ich im Projekt begonnen habe. (Das hat natürlich nichts mit dem Geschäft zu tun.) const _ = erfordern('lodash'); const echo = async (i) => { setzeTimeout(() => { Konsole.log('i===>', i); }, 5000); } sei arrs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const task = async () => { _.forEach(arrs, async (i) => { warte auf Echo(i); }) } const run = async () => { Konsole.log('run-start====>date:', neues Date().toLocaleDateString()) warte auf Aufgabe(); Konsole.log('run-end====>date:', neues Date().toLocaleDateString()) } (asynchron () => { console.log('starten...') warte auf Ausführung(); console.log('Ende ...') })() // Start... // Lauf-Start====>Datum: 25.08.2018 // Laufende====>Datum: 25.08.2018 // Ende... // ich ===> 1 // ich ===> 2 // ich ===> 3 // ich ===> 4 // ich ===> 5 // ich ===> 6 // ich ===> 7 // ich ===> 8 // ich ===> 9 Der obige Code und die Ausgabe wurden angegeben. Es ist seltsam, dass das Warten hier keine Wirkung hat. Zuerst gab es ein Problem mit meinem Geschäftscode, weil ich das Geschäft hinzugefügt habe, und dann habe ich den Code extrahiert, aber es hat immer noch nicht funktioniert. Zu diesem Zeitpunkt habe ich wirklich an dem Warten gezweifelt. Abschließend wird die Antwort auf die Frage gegeben:lodashs forEach und [].forEach unterstützen await nicht. Wenn Sie await während des Durchlaufens ausführen müssen, können Sie for-of verwenden. Hier ist der richtige Code: const _ = erfordern('lodash'); const echo = async (i) => { returniere neues Promise((lösen,ablehnen)=>{ setzeTimeout(() => { console.log('i===>', i, neues Date().toLocaleTimeString()); Entschlossenheit (i); }, 2000); }) } sei arrs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const task = async () => { // _.forEach(arrs, async (i) => { // warte auf echo(ji); // }) // arrs.forEach(async (i)=> { // warte auf echo(i); // }); für (const i von arrs) { warte auf Echo(i); } } const run = async () => { Konsole.log('run-start====>date:', neues Date().toLocaleDateString()) warte auf Aufgabe(); Konsole.log('run-end====>date:', neues Date().toLocaleDateString()) } (asynchron () => { console.log('starten...') warte auf Ausführung(); console.log('Ende ...') })() // Ausgabe starten... Lauf-Start====>Datum: 26.8.2018 ich===> 1 20:51:29 ich===> 2 20:51:31 ich===> 3 20:51:33 ich===> 4 20:51:35 ich===> 5 20:51:37 ich===> 6 20:51:39 ich===> 7 20:51:42 ich===> 8 20:51:44 ich===> 9 20:51:46 ich===> 10 20:51:48 Laufende====>Datum: 26.8.2018 Ende... AbschlussBeim Lösen eines Problems kann man manchmal das Ausschlussverfahren anwenden. In diesem Beispiel wissen wir beispielsweise, dass der Wartemechanismus einwandfrei sein muss. Wenn es ein Problem gibt, bin ich definitiv nicht an der Reihe, es zu testen. Das verbleibende Problem kann also nur die Ursache für die For-Durchquerung sein. Da ich es am Anfang mit lodash implementiert habe, könnte ich mich fragen, ob lodashs forEach keine (oder redundante) Await-Verarbeitung durchgeführt hat. Zu diesem Zeitpunkt könnte ich es auf eine andere Weise versuchen. Im Allgemeinen ist es eine Frage der Erfahrung. Ergänzung: Probleme bei der Verwendung von async/await in forEach 1. ProblembeschreibungVor einigen Tagen ist bei mir im Projekt ein asynchrones JavaScript-Problem aufgetreten: Es gibt einen Datensatz, von dem jeder asynchron verarbeitet werden muss, und es ist zu hoffen, dass die Verarbeitung synchron erfolgt. Die Codebeschreibung lautet wie folgt: // Daten generieren const getNumbers = () => { returniere Promise.resolve([1, 2, 3]) } // Asynchrone Verarbeitung const doMulti = num => { returniere neues Promise((lösen, ablehnen) => { setzeTimeout(() => { wenn (Zahl) { auflösen(Zahl * Zahl) } anders { ablehnen(neuer Fehler('Nummer nicht angegeben')) } }, 2000) }) } // Hauptfunktion const main = async () => { Konsole.log('Start'); Konstantennummern = [1, 2, 3]; nums.forEach(async (x) => { const res = warte auf doMulti(x); konsole.log(res); }); konsole.log('Ende'); }; // main() ausführen; In diesem Beispiel iteriert forEach über jede Zahl und führt die doMulti-Operation aus. Das Ergebnis der Codeausführung ist: Zuerst werden Start und Ende sofort ausgedruckt. Nach 2 Sekunden werden 1, 4 und 9 gleichzeitig ausgegeben. Dieses Ergebnis unterscheidet sich etwas von dem, was wir erwartet haben. Wir hoffen, alle 2 Sekunden eine asynchrone Verarbeitung durchzuführen und nacheinander 1, 4 und 9 auszugeben. Der aktuelle Code sollte also parallel ausgeführt werden, wir erwarten jedoch eine seriell ausgeführte Ausführung. Versuchen wir, die forEach-Schleife durch eine for-Schleife zu ersetzen: const main = async () => { Konsole.log('Start'); const nums = warte auf getNumbers(); für (const x von Nums) { const res = warte auf doMulti(x); konsole.log(res); } konsole.log('Ende'); }; Das Ausführungsergebnis ist genau wie erwartet: Die Ausgabe lautet: Start, 1, 4, 9, Ende. 2. ProblemanalyseDie Ideen sind dieselben, aber die verwendeten Durchquerungsmethoden sind unterschiedlich. Warum ist das so? Ich habe auf MDN nach dem Polyfill für forEach gesucht. Referenz MDN-Array.prototype.forEach(): // Produktionsschritte von ECMA-262, Ausgabe 5, 15.4.4.18 // Referenz: http://es5.github.io/#x15.4.4.18 wenn (!Array.prototype.forEach) { Array.prototype.forEach = Funktion(Rückruf, diesesArg) { var T, k; wenn (dies == null) { throw new TypeError('dies ist null oder nicht definiert'); } // 1. O sei das Ergebnis des Aufrufs von toObject() und übergibt die // |diesen| Wert als Argument. var O = Objekt(dieses); // 2. Lassen Sie lenValue das Ergebnis des Aufrufs von Get() internal sein. // Methode von O mit dem Argument „Länge“. // 3. Lass len toUint32(lenValue) sein. var len = O.Länge >>> 0; // 4. Wenn isCallable(callback) false ist, werfen Sie eine TypeError-Ausnahme. // Siehe: http://es5.github.com/#x9.11 wenn (Typ des Rückrufs !== "Funktion") { throw new TypeError(callback + ' ist keine Funktion'); } // 5. Wenn thisArg angegeben wurde, lass T thisArg sein; sonst lass // T ist nicht definiert. if (Argumente.Länge > 1) { T = diesesArg; } // 6. Sei k 0 k = 0; // 7. Wiederholen, solange k < len während (k < Länge) { var kWert; // a. Lassen Sie Pk ToString(k) sein. // Dies ist implizit für LHS-Operanden des in-Operators // b. Lassen Sie kPresent das Ergebnis des Aufrufs von HasProperty sein // interne Methode von O mit Argument Pk. // Dieser Schritt kann mit c kombiniert werden // c. Wenn kPresent wahr ist, dann wenn (k in O) { // i. Lassen Sie kValue das Ergebnis des Aufrufs von Get internal sein. // Methode von O mit Argument Pk. kWert = O[k]; // ii. Rufen Sie die interne Callback-Methode mit T als // dieser Wert und die Argumentliste, die kValue, k und O enthält. Rückruf.call(T, kValue, k, O); } // d. Erhöhe k um 1. k++; } // 8. Rückgabe undefiniert }; } Aus Schritt 7 im obigen Polyfill können wir die folgenden Schritte einfach verstehen: Array.prototype.forEach = Funktion (Rückruf) { // dies stellt unser Array dar für (let index = 0; index < this.length; index++) { // Wir rufen den Callback für jeden Eintrag auf Rückruf(dies[Index], Index, dies); }; }; Dies entspricht einer For-Schleife, die diese asynchrone Funktion ausführt. Sie wird also parallel ausgeführt, wodurch alle Ausgabeergebnisse gleichzeitig ausgegeben werden: 1, 4, 9. const main = async () => { Konsole.log('Start'); const nums = warte auf getNumbers(); // nums.forEach(async (x) => { // const res = warte auf doMulti(x); // konsole.log(res); // }); für (let index = 0; index < nums.length; index++) { (async x => { const res = warte auf doMulti(x) Konsole.log(res) })(Zahlen[Index]) } konsole.log('Ende'); }; 3. LösungJetzt haben wir das Problem klar analysiert. In der vorherigen Lösung haben wir die for-of-Schleife anstelle von forEach verwendet. Tatsächlich können wir forEach auch ändern: const asyncForEach = async (Array, Rückruf) => { für (let index = 0; index < array.length; index++) { warte auf Rückruf (Array[Index], Index, Array); } } const main = async () => { Konsole.log('Start'); const nums = warte auf getNumbers(); warte auf asyncForEach(nums, async x => { const res = warte auf doMulti(x) Konsole.log(res) }) konsole.log('Ende'); }; hauptsächlich(); IV. Eslint-ProblemeZu diesem Zeitpunkt meldete Eslint einen weiteren Fehler: „no-await-in-loop“. Zu diesem Punkt wird es auch im offiziellen Eslint-Dokument https://eslint.org/docs/rules/no-await-in-loop erklärt. Gutes Schreiben: asynchrone Funktion foo(Dinge) { const Ergebnisse = []; für (const Ding von Dingen) { // Gut: Alle asynchronen Operationen werden sofort gestartet. Ergebnisse.push(Bar(Ding)); } // Da nun alle asynchronen Vorgänge ausgeführt werden, warten wir hier, bis sie alle abgeschlossen sind. returniere baz(warte auf Promise.all(Ergebnisse)); } Schlechtes Schreiben: asynchrone Funktion foo(Dinge) { const Ergebnisse = []; für (const Ding von Dingen) { // Schlecht: jede Schleifeniteration wird verzögert, bis der gesamte asynchrone Vorgang abgeschlossen ist Ergebnisse.push(warte auf Balken(Ding)); } gib baz(Ergebnisse) zurück; } Tatsächlich gibt es zwischen den beiden oben genannten Schreibweisen keinen Unterschied im Guten oder Schlechten. Die Ergebnisse dieser beiden Schreibweisen sind völlig unterschiedlich. Die von Eslint empfohlene „gute Schreibmethode“ hat bei der Ausführung asynchroner Vorgänge keine Reihenfolge, während die „schlechte Schreibmethode“ eine Reihenfolge hat. Die zu verwendende spezifische Schreibmethode sollte basierend auf den Geschäftsanforderungen bestimmt werden. Daher erwähnt Eslint im Abschnitt „Wann sollte es nicht verwendet werden“ des Dokuments auch, dass wir diese Regel deaktivieren können, wenn eine sequentielle Ausführung erforderlich ist:
Das Obige ist meine persönliche Erfahrung. Ich hoffe, es kann Ihnen als Referenz dienen. Ich hoffe auch, dass Sie 123WORDPRESS.COM unterstützen werden. Sollten dennoch Fehler oder unvollständige Überlegungen vorliegen, freue ich mich über eine Korrektur. Das könnte Sie auch interessieren:
|
>>: So stellen Sie DoNetCore mit Nginx in der Alibaba Cloud bereit
Horizontales Scrollen ist nicht in allen Situation...
Wenn Sie eine virtuelle Maschine verwenden, stell...
1. Hintergrund Im Allgemeinen verwenden wir für D...
In diesem Artikel finden Sie das Installations- u...
In diesem Artikelbeispiel wird der spezifische Co...
Vorwort: js ist eine Single-Thread-Sprache, daher...
TypeScript-Bündelung Webpack-Integration Normaler...
Xiaobai zeichnet die Installation von vmtools auf...
BinLog BinLog ist ein Binärprotokoll, das alle Än...
Frage Wie ändere ich den CSS-Pseudoklassenstil mi...
Vorwort Excel ist leistungsstark und weit verbrei...
Inhaltsverzeichnis Was ist passiert? Verwendung S...
Jeder, der das Linux-System verwendet hat, sollte...
In der neuesten Version von WIN10 hat Microsoft e...
html <!DOCTYPE html> <html lang="de...