Detaillierte Erklärung der Nodejs-Array-Warteschlange und der forEach-Anwendung

Detaillierte Erklärung der Nodejs-Array-Warteschlange und der forEach-Anwendung

In diesem Artikel werden hauptsächlich die Probleme und Lösungen beschrieben, die durch die im Nodejs-Entwicklungsprozess auftretenden Array-Eigenschaften verursacht werden, sowie die flexible Anwendung von Arrays.

Die Testergebnisse dieses Artikels basieren auf Node v6.9.5

Arrays und Warteschlangen

Mit der Push/Shift-Methode des Array-Objekts kann beispielsweise die First-In-First-Out-Funktion der Warteschlange implementiert werden:

>ein=[]
[]
>a.drücken(2.3.4)
3
>a.drücken(2)
3
>eine
[2.3.4.2]
>a.verschiebung()
2
>eine
>[3.4.2]

Arrays und forEach

Es gibt zwei gängige Möglichkeiten, ein Array zu löschen: Löschen und die Verwendung der Splice-Methode. Der Unterschied zwischen ihnen muss deutlich gemacht werden.

Vorgehensweise/Methode veranschaulichen
spleißen Löscht und gibt das angegebene Array-Element zurück. Die Länge des Arrays selbst ändert sich, aber das Elementobjekt wird nicht freigegeben.
löschen Löschen (Freigeben) des Elementobjekts, das Array-Element bleibt unverändert und der Wert wird undefiniert

Wenn Sie ein Element vollständig aus einem Array entfernen möchten, verwenden Sie splice:

> a=[1,2,3]
[ 1, 2, 3 ]
> ein.spleißen(1,1)
[ 2 ]
> eine
[ 1, 3 ]
> a.Länge
2
> a.fürJeden(Funktion(Element, Index){Konsole.info("Index[", Index,"]:", Element)});
Index[ 0 ]: 1
index[ 1 ]: 3
undefiniert
>

Welchen Effekt hat also die Ausführung von forEach nach dem Löschen eines Elementobjekts mit delete?

forEachs Verarbeitungsmechanismus für Arrays mit leeren Elementen

Die Testergebnisse lauten wie folgt

> a=[1,2,3]
[ 1, 2, 3 ]
> lösche a[1]
WAHR
> eine
[ 1, , 3 ]
> a.Länge
3
> a.fürJeden(Funktion(Element, Index){Konsole.info("Index[", Index,"]:", Element)});
Index[ 0 ]: 1
index[ 2 ]: 3
undefiniert

Den Testergebnissen zufolge durchläuft forEach keine Elemente, deren Wert nicht definiert ist. In praktischen Anwendungen ist es eine große Herausforderung, festzustellen, ob forEach beendet ist.

Um die asynchrone Funktionsanwendung von forEach zu lösen, können Sie dem Array einen Prototyp hinzufügen, um gültige Daten selbst zu verwalten und festzulegen.

Die Wirkung ist wie folgt:

> a=[1,2,3]
[ 1, 2, 3 ]
> a.gültigeNummer=3
3
> lösche a[2]
WAHR
> a.gültigeNummer=2
2
> eine
[ 1, 2, , gültige Nummer: 2 ]
> a.Länge
3
> a.gültige Nummer
2
> a.fürJeden(Funktion(Element, Index){Konsole.info("Index[", Index,"]:", Element)});
Index[ 0 ]: 1
index[ 1 ]: 2
undefiniert
>

Ergänzung: Node.js-Array forEach verarbeitet Kontextanweisungen synchron

Ich bin an die Denkweise der C-Sprache gewöhnt und als ich zum ersten Mal mit Node.js in Berührung kam, bereitete mir die asynchrone Verarbeitung Kopfschmerzen.

Beim Schreiben von Code kann es vorkommen, dass Sie die Elemente eines Arrays in einer Schleife verarbeiten und dann nach Abschluss der gesamten Verarbeitung eine letzte Operation ausführen müssen. Aufgrund der asynchronen Natur von JS wird diese letzte Anweisung jedoch zuerst ausgeführt. Nehmen Sie sich also etwas Zeit, um forEach zu studieren.

Reden ist billig. Zeigen Sie mir den Code.

forEach-Verwendung

forEach wird zum Durchlaufen der Array-Struktur verwendet. Ich habe jemanden sagen hören, dass forEach auf der untersten Ebene mithilfe von for implementiert wird. Ich habe es nicht eingehend untersucht, aber zumindest scheint der Effekt derselbe zu sein. Die drei Parameter der Rückruffunktion von forEach sind: Wert, Sequenznummer und ursprüngliches Array. Die Sequenznummer beginnt bei 0.

(() => {
  sei arr = [2, 3, 1];
  arr.forEach(Funktion (Wert, Index, Array) {
    konsole.log(Wert);
    konsole.log(index);
    Konsole.log(Array);
    konsole.log('-----');
  });
})();

Ausgabe

2
0
[ 2, 3, 1 ]
-----
3
1
[ 2, 3, 1 ]
-----
1
2
[ 2, 3, 1 ]
-----

Aus den Ergebnissen ist ersichtlich, dass die mehreren Schleifen von forEach synchronisiert sind, das heißt, sie werden nacheinander ausgeführt. Wenn ich aber daran denke, dass es sich um JS handelt, erscheint mir eine Synchronisierung unmöglich. . Sie können es überprüfen.

forEach verarbeitet asynchron mehrere Schleifen

Dieses Mal wird eine Timer-Aufgabe zu forEach hinzugefügt und jeder Schleifenvorgang wird um die mit dem Wert verbundene Zeit verzögert, um einen zeitaufwändigeren Vorgang zu simulieren.

(() => {
  sei arr = [2, 3, 1];
  arr.forEach(Funktion (Wert, Index, Array) {
    setzeTimeout(Funktion () {
      konsole.log(Wert);
    }, Wert*100);
  });
})();

Ausgabe

1
2
3

Aus den Ergebnissen können wir ersehen, dass die Aufgabe mit der kürzesten Zeit zuerst abgeschlossen wird und die Aufgaben jeder Schleife nicht in der Reihenfolge der Schleife ausgeführt werden, d. h. mehrere Schleifen werden asynchron verarbeitet.

forEach-Kontext wird ebenfalls asynchron ausgeführt

Zurück zum eingangs erwähnten Problem: Unabhängig davon, ob die mehreren Schleifen nacheinander ausgeführt werden, muss ich nach Abschluss aller Aufgaben in forEach ein Datenelement ausführen, um mich darüber zu benachrichtigen, dass alle Aufgaben abgeschlossen sind.

(() => {
  sei arr = [2, 3, 1];
  arr.forEach(Funktion (Wert, Index, Array) {
    setzeTimeout(Funktion () {
      konsole.log(Wert);
    }, Wert*100);
  });
  console.log('Die ganze Arbeit ist erledigt');
})();

Ausgabe

Die ganze Arbeit ist erledigt
1
2
3

Den Ergebnissen zufolge sind auch die Kontextanweisungen nicht synchronisiert. Die Aufgabe in der forEach-Schleife wird nicht abgeschlossen, bevor die Meldung angezeigt wird, dass alle Aufgaben abgeschlossen sind, was offensichtlich nicht den Erwartungen entspricht.

Ich habe viele Blogs zu diesem Problem gelesen, konnte aber keine passende Lösung finden. Schließlich konnte ich diese Funktion nur mit Promise.all implementieren.

Promise.all implementiert die synchrone Verarbeitung von forEach-Kontextanweisungen

Ändern Sie den obigen Code in die Promise.all-Struktur. Am Ende jeder Schleife wird resolve() aufgerufen. Wir wissen, dass die then-Funktion von Promise.all nur ausgelöst wird, wenn alle Promises ausgeführt wurden, was unseren Anforderungen zu entsprechen scheint.

(() => {
  sei arr = [2, 3, 1];
  sei proArr = [];
  arr.forEach(Funktion (Wert, Index) {
    proArr[index] = neues Promise(Funktion (auflösen) {
      setzeTimeout(Funktion () {
        konsole.log(Wert);
        lösen();
      }, Wert*100);
    });
  });
  Versprechen.alles(proArr).then(()=>{
    console.log('Die ganze Arbeit ist erledigt');
  })
})();

Ausgabe

1
2
3
Die ganze Arbeit ist erledigt

Den Ergebnissen zufolge wurden unsere Anforderungen erfüllt.

Mögliche Probleme

Als ich über die asynchronen Eigenschaften von JS nachdachte, wurde mir plötzlich klar, dass bei dieser Methode möglicherweise ein Problem vorliegt.

Hier wird bei jedem Aufruf von forEach das Promise-Array zugewiesen. Dieser Vorgang sollte sehr kurz sein. Die letzte Promise.all-Anweisung wird erst aufgerufen, nachdem die Zuweisung in den drei Schleifen abgeschlossen ist.

Wenn das Array jedoch sehr groß ist und die Zuweisungsoperation in der Schleife sehr zeitaufwändig ist und nur die Hälfte der Zuweisungsoperation abgeschlossen ist, ist das bei der Ausführung des letzten Promise.all übergebene Promise-Array möglicherweise nicht das Array, das alle Promises enthält.

In diesem Fall wartet Promise.all nur auf die Hälfte der Vorgänge. Wenn Promise.all wartet, ist nicht bekannt, ob auf das am Ende des Arrays zugewiesene Promise gewartet wird.

Ich habe gerade erst mit der Verwendung von JS begonnen und verstehe den Implementierungsmechanismus nicht. Daher kann ich nur experimentieren, um zu überprüfen, ob dieses Problem besteht. Als nächstes vergrößern wir dieses Array. Bitte verzeihen Sie mir, dass ich den sichersten Weg gewählt habe, um es zu vergrößern.

(() => {
  sei arr = [2, 3, 1, 2, 3, 1, 2, 3, 1, 2]; // 10
  arr = arr.concat(arr); // 2^1 * 10
  arr = arr.concat(arr); // 2^2 * 10
  arr = arr.concat(arr); // 2^3
  arr = arr.concat(arr); // 2^4
  arr = arr.concat(arr); // 2^5
  arr = arr.concat(arr);
  arr = arr.concat(arr);
  arr = arr.concat(arr);
  arr = arr.concat(arr);
  arr = arr.concat(arr); // 2^10
  arr = arr.concat(arr);
  arr = arr.concat(arr);
  arr = arr.concat(arr);
  arr = arr.concat(arr);
  arr = arr.concat(arr); // 2^15
  arr = arr.concat(arr);
  arr = arr.concat(arr); // 2^17 * 10
// arr = arr.concat(arr); // 2^18 * 10
  Konsole.log(arr.Länge);
  sei proArr = [];
  arr.forEach(Funktion (Wert, Index) {
    proArr[index] = neues Promise(Funktion (auflösen) {
      setzeTimeout(Funktion () {
        konsole.log(Wert);
        lösen();
      }, Wert*100);
    });
  });
  Versprechen.alles(proArr).then(()=>{
    console.log('Die ganze Arbeit ist erledigt');
    Konsole.log(arr.Länge);
  }).catch(Funktion (Fehler) {
    console.log(fehler);
  })
})();

Nach dem Testen auf meinem Computer meldet Promise bei einer Array-Länge von 2^18 * 10 einen RangeError-Fehler: Zu viele Elemente an Promise.all übergeben.

Wenn die Array-Länge 2^17 * 10 oder 2621440 beträgt, wird es normal ausgeführt. Nach mehrmaligem Testen wird die Ausgabe des zuletzt ausgeführten Befehls „Die gesamte Arbeit ist erledigt“ immer am Ende ausgegeben (da der Terminalpuffer zu klein ist, wird das Ausgabeergebnis mithilfe der Umleitung node xx.js > log.txt in eine Datei zur Anzeige umgeleitet).

Natürlich wird es in der Anwendung kein so großes Array geben. Den Ergebnissen nach zu urteilen, treten die oben genannten Probleme in tatsächlichen Anwendungen nicht auf.

Das heißt, Promise.all kann verwendet werden, um die synchrone Verarbeitung von forEach-Kontextanweisungen zu implementieren.

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:
  • Verwendung des Node.js-HTTP-Moduls
  • Nodejs Exploration: Tiefgreifendes Verständnis des Prinzips der Single-Threaded High Concurrency
  • Es ist ganz einfach zu verstehen, was Node.js ist
  • Spezifische Verwendung globaler Variablen von node.js
  • Asynchroner Lebenszyklus von AsyncHooks in Node8
  • Nodejs-Fehlerbehandlungsprozessaufzeichnung
  • Der gesamte Prozess der Verwendung von node.js Express zum automatischen Erstellen des Projekts
  • So verwenden Sie Shell-Skripte in Node
  • Der Kernprozess der NodeJS-Verarbeitung einer TCP-Verbindung
  • Vergleich zwischen Node.js und Deno

<<:  MySQL-Trigger: ausführliche Erklärung und einfaches Beispiel

>>:  Beispielcode für die Batchbereitstellung von Nginx mit Ansible

Artikel empfehlen

jQuery+Ajax zum Erreichen eines einfachen Paging-Effekts

In diesem Artikel wird der spezifische Code von j...

Vue3 implementiert ein Beispiel für eine Nachrichtenkomponente

Inhaltsverzeichnis Komponentendesign Definieren d...

Detaillierte Erläuterung der Nginx-Zugriffsbeschränkungskonfiguration

Was ist die Nginx-Zugriffsbeschränkungskonfigurat...

MySQL Community Server 5.7.19 Installationshandbuch (detailliert)

Link zum Download der ZIP-Datei auf der offiziell...

Javascript zum Wechseln von Bildern per Mausklick

In diesem Artikelbeispiel wird der spezifische Ja...

Beispiel für eine MySQL-DML-Sprachoperation

Zusätzliche Erklärung, Fremdschlüssel: Verwenden ...

Konfigurieren Sie ein Implementierungsbeispiel für den Mysql-Master-Slave-Dienst

Konfigurieren Sie ein Implementierungsbeispiel fü...

Eine kurze Diskussion über den CSS-Kaskadierungsmechanismus

Warum hat CSS einen Kaskadierungsmechanismus? Da ...

Häufig verwendete JavaScript-Array-Methoden

Inhaltsverzeichnis 1. filter() 2. fürJedes() 3. e...

HTML-Lernhinweise – Detaillierte Erklärung der HTML-Syntax (unbedingt lesen)

1. Was ist die HTML-Auszeichnungssprache? HTML is...

Einführung in gängige XHTML-Tags

<br />Ich habe festgestellt, dass viele Leut...

Anwendung und Implementierung des Datencache-Mechanismus für kleine Programme

Informationen zum Miniprogramm-Datencache Datenca...

React kapselt die globale Bullet-Box-Methode

In diesem Artikelbeispiel wird der spezifische Co...

Einige Fähigkeiten, die Sie beim Erstellen von Webseiten kennen müssen

1. Z-Index ist in IE6 ungültig. In CSS wird die E...