Ich habe eine Frage zur nativen Array.forEach
Implementierung von JavaScript: Verhält es sich asynchron? Zum Beispiel, wenn ich anrufe:
[many many elements].forEach(function () {lots of work to do})
Wird dies nicht blockierend sein?
javascript
arrays
asynchronous
foreach
node.js
R. Gr.
quelle
quelle
Antworten:
Nein, es blockiert. Schauen Sie sich das an Spezifikation des Algorithmus an .
Auf MDN wird jedoch eine möglicherweise leichter verständliche Implementierung angegeben :
Wenn Sie für jedes Element viel Code ausführen müssen, sollten Sie einen anderen Ansatz wählen:
und dann nenne es mit:
Dies wäre dann nicht blockierend. Das Beispiel stammt aus High Performance JavaScript .
Eine andere Option könnten Web-Worker sein .
quelle
forEach
ist nicht blockieren aufawait
Aussagen zum Beispiel , und Sie sollten lieber einen verwendenfor
Schleife: stackoverflow.com/questions/37962880/...await
Innenfunktionenasync
verwenden. WeißforEach
aber nicht, was asynchrone Funktionen sind. Denken Sie daran, dass asynchrone Funktionen nur Funktionen sind, die ein Versprechen zurückgeben. Würden Sie erwartenforEach
, ein vom Rückruf zurückgegebenes Versprechen zu erfüllen?forEach
ignoriert den Rückgabewert aus dem Rückruf vollständig. Es wäre nur in der Lage, einen asynchronen Rückruf zu verarbeiten, wenn er selbst asynchron wäre.Wenn Sie eine asynchrone Version von
Array.forEach
und ähnlichem benötigen, sind diese im asynchronen Modul von Node.j verfügbar: http://github.com/caolan/async ... als Bonus funktioniert dieses Modul auch im Browser .quelle
eachSeries
stattdessen verwenden.Es gibt ein allgemeines Muster für eine wirklich umfangreiche Berechnung in Node, das möglicherweise auf Sie zutrifft ...
Der Knoten ist Single-Threaded (als bewusste Entwurfsauswahl siehe Was ist Node.js? ). Dies bedeutet, dass nur ein einziger Kern verwendet werden kann. Moderne Boxen haben 8, 16 oder sogar mehr Kerne, so dass 90 +% der Maschine im Leerlauf bleiben können. Das übliche Muster für einen REST-Service besteht darin, einen Knotenprozess pro Kern zu starten und diese hinter einen lokalen Load Balancer wie http://nginx.org/ zu stellen .
Ein Kind gabeln - Für das, was Sie versuchen, gibt es ein anderes allgemeines Muster: das Verzweigen eines Kinderprozesses, um das schwere Heben auszuführen. Der Vorteil ist, dass der untergeordnete Prozess im Hintergrund umfangreiche Berechnungen durchführen kann, während der übergeordnete Prozess auf andere Ereignisse reagiert. Der Haken ist, dass Sie mit diesem untergeordneten Prozess keinen Speicher teilen können / sollten (nicht ohne viele Verzerrungen und nativen Code). Sie müssen Nachrichten weitergeben. Dies funktioniert hervorragend, wenn die Größe Ihrer Eingabe- und Ausgabedaten im Vergleich zu der durchzuführenden Berechnung gering ist. Sie können sogar einen untergeordneten node.js-Prozess starten und denselben Code verwenden, den Sie zuvor verwendet haben.
Zum Beispiel:
quelle
Array.forEach
ist für Computer gedacht, die nicht warten, und es gibt nichts zu gewinnen, wenn Berechnungen in einer Ereignisschleife asynchron gemacht werden (Webworker fügen Multiprocessing hinzu, wenn Sie Multi-Core-Berechnungen benötigen). Wenn Sie auf das Ende mehrerer Aufgaben warten möchten, verwenden Sie einen Zähler, den Sie in eine Semaphorklasse einschließen können.quelle
Edit 2018-10-11: Es sieht so aus, als ob es eine gute Chance gibt, dass der unten beschriebene Standard nicht durchgeht. Betrachten Sie Pipelineing als Alternative (verhält sich nicht genau gleich, aber Methoden könnten auf ähnliche Weise implementiert werden).
Genau aus diesem Grund freue ich mich über es7. In Zukunft können Sie so etwas wie den folgenden Code ausführen (einige der Spezifikationen sind nicht vollständig. Verwenden Sie sie daher mit Vorsicht, ich werde versuchen, sie auf dem neuesten Stand zu halten). Grundsätzlich können Sie mit dem Operator new :: bind eine Methode für ein Objekt ausführen, als ob der Prototyp des Objekts die Methode enthält. zB [Object] :: [Method], wo Sie normalerweise [Object] aufrufen würden. [ObjectsMethod]
Hinweis dies heute zu tun (24-Jul-16) und hat es in allen Browsern müssen Sie Ihren Code für die folgende Funktionalität transpile: Import / Export , Pfeil Funktionen , Promises , Async / Await und vor allem Funktion binden . Der folgende Code kann so geändert werden, dass nur die Funktion bind verwendet wird, wenn dies erforderlich ist. All diese Funktionen sind heute mithilfe von babel verfügbar .
YourCode.js (wo ' viel zu erledigende Arbeit ' einfach ein Versprechen zurückgeben muss, um es zu lösen, wenn die asynchrone Arbeit erledigt ist.)
ArrayExtensions.js
quelle
Dies ist eine kurze asynchrone Funktion, die verwendet werden kann, ohne dass Bibliotheken von Drittanbietern erforderlich sind
quelle
Auf npm gibt es ein Paket für einfaches Asynchronisieren für jede Schleife .
Auch eine andere Variante für AllAsync
quelle
Es ist möglich, auch die Lösung wie folgt zu codieren:
Andererseits ist es viel langsamer als ein "für".
Andernfalls kann die hervorragende Async-Bibliothek dies tun: https://caolan.github.io/async/docs.html#each
quelle
Hier ist ein kleines Beispiel, das Sie ausführen können, um es zu testen:
Es wird so etwas erzeugen (wenn es zu wenig / zu viel Zeit dauert, erhöhen / verringern Sie die Anzahl der Iterationen):
quelle
Verwenden Sie Promise.each der Bluebird- Bibliothek.
Diese Methode iteriert über ein Array oder ein Versprechen eines Arrays, das Versprechen (oder eine Mischung aus Versprechen und Werten) mit der angegebenen Iteratorfunktion mit der Signatur (Wert, Index, Länge) enthält, wobei der Wert der aufgelöste Wert von a ist jeweiliges Versprechen im Eingabearray. Die Iteration erfolgt seriell.Wenn die Iteratorfunktion ein Versprechen oder ein Thenable zurückgibt, wird das Ergebnis des Versprechens erwartet, bevor mit der nächsten Iteration fortgefahren wird. Wenn ein Versprechen im Eingabearray abgelehnt wird, wird auch das zurückgegebene Versprechen abgelehnt.
Wenn alle Iterationen erfolgreich aufgelöst wurden, wird Promise.each unverändert in das ursprüngliche Array aufgelöst . Wenn jedoch eine Iteration abgelehnt wird oder Fehler auftreten, stellt Promise.each die Ausführung sofort ein und verarbeitet keine weiteren Iterationen. Der Fehler oder abgelehnte Wert wird in diesem Fall anstelle des ursprünglichen Arrays zurückgegeben.
Diese Methode soll bei Nebenwirkungen angewendet werden.
quelle