So implementieren Sie Parallelitätskontrolle in JavaScript

So implementieren Sie Parallelitätskontrolle in JavaScript

1. Einführung in die Parallelitätskontrolle

Angenommen, es stehen 6 ausstehende Aufgaben zur Ausführung an und wir möchten die Anzahl der Aufgaben begrenzen, die gleichzeitig ausgeführt werden können, d. h., es können höchstens 2 Aufgaben gleichzeitig ausgeführt werden. Wenn eine beliebige Aufgabe in der Liste der ausgeführten Aufgaben abgeschlossen ist, ruft das Programm automatisch eine neue zu erledigende Aufgabe aus der Liste der zu erledigenden Aufgaben ab und fügt die Aufgabe der Liste der ausgeführten Aufgaben hinzu. Damit jeder den obigen Vorgang intuitiver verstehen kann, hat Bruder Abao speziell die folgenden 3 Bilder gezeichnet:

1.1 Phase 1

1.2 Phase 2

1.3 Phase 3

Okay, nachdem ich die Parallelitätssteuerung vorgestellt habe, werde ich die Async-Pool-Bibliothek auf Github verwenden, um die spezifische Implementierung der asynchronen Task-Parallelitätssteuerung vorzustellen.

https://github.com/rxaviers/async-pool

Führen Sie mehrere Promise-Returning- und asynchrone Funktionen mit begrenzter Parallelität unter Verwendung von nativem ES6/ES7 aus.

2. Implementierung der Parallelitätskontrolle

Die async-pool-Bibliothek bietet zwei verschiedene Implementierungsversionen: ES7 und ES6. Bevor wir die spezifische Implementierung analysieren, schauen wir uns an, wie sie verwendet wird.

2.1 Verwendung von asyncPool

const timeout = i => neues Versprechen(auflösen => setTimeout(() => auflösen(i), i));
warte auf asyncPool(2, [1000, 5000, 3000, 2000], Timeout);

Im obigen Code verwenden wir die von der Async-Pool-Bibliothek bereitgestellte Funktion AsyncPool, um die gleichzeitige Steuerung asynchroner Aufgaben zu implementieren. Die Signatur der Funktion asyncPool lautet wie folgt:

Funktion asyncPool(PoolLimit, Array, IteratorFn){ ... }

Diese Funktion erhält 3 Parameter:

  • poolLimit (Zahlentyp): gibt die Anzahl der zu begrenzenden gleichzeitigen Verbindungen an;
  • Array (Array-Typ): stellt ein Task-Array dar;
  • iteratorFn (Funktionstyp): stellt die Iterationsfunktion dar, die zum Verarbeiten jedes Aufgabenelements verwendet wird. Die Funktion gibt ein Promise-Objekt oder eine asynchrone Funktion zurück.

Für das obige Beispiel sieht der entsprechende Ausführungsprozess nach Verwendung der Funktion asyncPool wie folgt aus:

const timeout = i => neues Versprechen(auflösen => setTimeout(() => auflösen(i), i));
warte auf asyncPool(2, [1000, 5000, 3000, 2000], Timeout);
//Iterator aufrufen (i = 1000)
//Iterator aufrufen (i = 5000)
// Pool-Limit von 2 erreicht, warten Sie, bis der Schnellere fertig ist …
// 1000 Abschlüsse
//Iterator aufrufen (i = 3000)
// Pool-Limit von 2 erreicht, warten Sie, bis der Schnellere fertig ist …
// 3000 Abschlüsse
//Iterator aufrufen (i = 2000)
// Iteration ist abgeschlossen. Warten Sie, bis die laufenden Iterationen abgeschlossen sind …
// 5000 Abschlüsse
// 2000 Zielankünfte
// Wird aufgelöst, Ergebnisse werden in der angegebenen Array-Reihenfolge `[1000, 5000, 3000, 2000]` übergeben.

Durch Beachten der obigen Kommentare können wir den Kontrollfluss innerhalb der Funktion asyncPool grob verstehen. Als nächstes analysieren wir die ES7-Implementierung der asyncPool-Funktion.

2.2 asyncPool ES7-Implementierung

asynchrone Funktion asyncPool(PoolLimit, Array, IteratorFn) {
  const ret = []; // Alle asynchronen Aufgaben speichern const executing = []; // Die asynchronen Aufgaben speichern, die ausgeführt werden für (const item of array) {
    // Rufen Sie die Funktion iteratorFn auf, um eine asynchrone Aufgabe zu erstellen. const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p); // Neue asynchrone Aufgaben speichern // Wenn der PoolLimit-Wert kleiner oder gleich der Gesamtzahl der Aufgaben ist, führe eine Parallelitätskontrolle durch, wenn (PoolLimit <= Array.Länge) {
      // Wenn die Aufgabe abgeschlossen ist, entfernen Sie die abgeschlossene Aufgabe aus dem Array der ausgeführten Aufgaben const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      executing.push(e); // Speichere die ausgeführte asynchrone Aufgabe, wenn (executing.length >= poolLimit) {
        warte auf Promise.race(executing); // Warte, bis die schnellere Aufgabe abgeschlossen ist }
    }
  }
  gibt Promise.all(ret) zurück;
}

Im obigen Code werden die Funktionen Promise.all und Promise.race vollständig genutzt und mit der in ES7 bereitgestellten asynchronen Wartefunktion kombiniert, um schließlich die Parallelitätssteuerungsfunktion zu realisieren. Mit der Anweisung „await Promise.race(executing);“ warten wir, bis die schnelleren Aufgaben in der Liste der ausgeführten Aufgaben abgeschlossen sind, bevor wir mit der nächsten Schleife fortfahren.

Die Implementierung von asyncPool ES7 ist relativ einfach. Als Nächstes sehen wir uns an, wie man die gleiche Funktionalität erreicht, ohne die Funktion „async await“ zu verwenden.

2.3 asyncPool ES6-Implementierung

Funktion asyncPool(PoolLimit, Array, IteratorFn) {
  sei i = 0;
  const ret = []; // Alle asynchronen Aufgaben speichern const executing = []; // In Ausführung befindliche asynchrone Aufgaben speichern const enqueue = function () {
    wenn (i === Array.Länge) {
      gibt Promise.resolve() zurück;
    }
    const item = array[i++]; // Neues Task-Element abrufen const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p);

    lass r = Promise.resolve();

    // Wenn der PoolLimit-Wert kleiner oder gleich der Gesamtzahl der Aufgaben ist, führen Sie eine Parallelitätskontrolle durch, wenn (PoolLimit <= Array.Länge) {
      // Wenn die Aufgabe abgeschlossen ist, entfernen Sie die abgeschlossene Aufgabe aus dem Array der ausgeführten Aufgaben const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      Ausführen von Push(e);
      wenn (Ausführungslänge >= PoolLimit) {
        r = Promise.race(wird ausgeführt);
       }
    }

     // Nachdem die schnelleren Aufgaben in der ausgeführten Aufgabenliste abgeschlossen sind, werden neue Aufgaben aus dem Array abgerufen array return r.then(() => enqueue());
  };
  : return enqueue().then(() => Promise.all(ret));
}

In der ES6-Implementierung wird die Kernsteuerungslogik durch die interne Enqueue-Funktion implementiert. Wenn das von Promise.race(executing) zurückgegebene Promise-Objekt abgeschlossen ist, wird die Enqueue-Funktion aufgerufen, um die neue zu erledigende Aufgabe aus dem Array abzurufen.

3. Bruder Abao hat etwas zu sagen

In den ES7- und ES6-Implementierungen der asyncPool-Bibliothek haben wir die Funktionen Promise.all und Promise.race verwendet. Darunter ist das handschriftliche Verfassen von „Promise.all“ eine häufige Frage im Vorstellungsgespräch. Bruder Abao nutzt einfach diese Gelegenheit und wird mit allen eine einfache Version der Funktionen Promise.all und Promise.race schreiben.

3.1 Handschriftliches Versprechen.alle

Die Methode Promise.all(iterable) gibt ein Promise-Objekt zurück. Wenn der Status aller eingegebenen Promise-Objekte aufgelöst wird, gibt das zurückgegebene Promise-Objekt die Ergebnisse jedes Promise-Objekts nach der Auflösung in Form eines Arrays zurück. Wenn der Status eines beliebigen eingegebenen Promise-Objekts auf „Abgelehnt“ gesetzt wird, wird das zurückgegebene Promise-Objekt mit der entsprechenden Fehlermeldung abgelehnt.

Promise.all = Funktion (Iteratoren) {
  returniere neues Promise((lösen, ablehnen) => {
    if (!Iteratoren || Iteratoren.Länge === 0) {
      lösen([]);
    } anders {
      let count = 0; // Zähler, der bestimmt, ob alle Aufgaben abgeschlossen wurden let result = []; // Ergebnis-Array for (let i = 0; i < iterators.length; i++) {
        // Da es sich bei iterators[i] um ein normales Objekt handeln kann, wird es als Promise-Objekt verpackt Promise.resolve(iterators[i]).then(
          (Daten) => {
            result[i] = data; // Speichere die entsprechenden Ergebnisse der Reihe nach // Wenn alle Aufgaben erledigt sind, gib die Ergebnisse einheitlich zurück if (++count === iterators.length) {
              Lösung (Ergebnis);
            }
          },
          (fehler) => {
            reject(err); // Wenn die Ausführung eines Promise-Objekts fehlschlägt, wird die Methode reject() aufgerufen. return;
          }
        );
      }
    }
  });
};

Es ist zu beachten, dass bei der Standardimplementierung von Promise.all der Parameter ein iterierbares Objekt wie Array, String oder Set ist.

3.2 Handschriftliches Promise.race

Die Methode Promise.race(iterable) gibt ein Promise-Objekt zurück. Sobald ein Promise-Objekt im Iterator aufgelöst oder abgelehnt wird, löst das zurückgegebene Promise-Objekt den entsprechenden Wert auf oder lehnt ihn ab.

Promise.race = Funktion (Iteratoren) {
  returniere neues Promise((lösen, ablehnen) => {
    für (const iter von Iteratoren) {
      Versprechen.lösen(iter)
        .then((res) => {
          Entschlossenheit (res);
        })
        .catch((e) => {
          ablehnen(e);
        });
    }
  });
};

In diesem Artikel analysiert Abao Ge detailliert die spezifische Implementierung der asynchronen Task-Parallelitätssteuerung von Async-Pool und ermöglicht gleichzeitig jedem ein besseres Verständnis des Kerncodes von Async-Pool. Schließlich leitete Bruder Abao alle an, eine einfache Version der Funktionen Promise.all und Promise.race zu schreiben. Tatsächlich gibt es neben der Funktion Promise.all noch eine weitere Funktion - Promise.allSettled, die zur Lösung der Probleme von Promise.all verwendet wird. Interessierte Freunde können sie selbst studieren.

IV. Referenzressourcen

Github - asynchroner Pool
MDN - Versprechen.all
MDN - Promise.race
MDN - Promise.allSettled

Oben finden Sie Einzelheiten zur Implementierung der Parallelitätssteuerung in JavaScript. Weitere Informationen zur JavaScript-Parallelitätssteuerung finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • So verwenden Sie Promise in JavaScript, um die Anzahl gleichzeitiger Anfragen zu steuern
  • ByteDance-Interview: So implementieren Sie mit JS die gleichzeitige Anforderungssteuerung von Ajax
  • Beispielcode zur Implementierung der gleichzeitigen Anforderungssteuerung in JavaScript/TypeScript
  • Beispielcode zur Implementierung der Parallelitätskontrolle mit JavaScript
  • Beispiel einer Methode zur Steuerung der gleichzeitigen Anzahl asynchroner JS-Schnittstellen
  • Fortgeschrittenes Tutorial zum Nodejs-Crawler – asynchrone Parallelitätskontrolle
  • Nodejs Praxiserfahrung: Eventproxy-Modul zur Steuerung der Parallelität

<<:  MySQL-FAQ-Serie: So vermeiden Sie eine plötzliche Vergrößerung der ibdata1-Datei

>>:  So verwenden Sie limit_req_zone in Nginx, um den Zugriff auf dieselbe IP zu beschränken

Artikel empfehlen

Vue elementUI implementiert Baumstrukturtabelle und Lazy Loading

Inhaltsverzeichnis 1. Ergebnisse erzielen 2. Back...

Beispielcode zur Implementierung der MySQL-Master-Slave-Replikation in Docker

Inhaltsverzeichnis 1. Übersicht 1. Grundsatz 2. U...

So verwenden Sie CSS-Medienabfragen mit einem geringeren Seitenverhältnis

CSS-Medienabfragen haben ein sehr praktisches Sei...

So berechnen Sie die Bildrate FPS von Webanimationen

Inhaltsverzeichnis Standards für flüssige Animati...

Analyse des Framework-Prinzips des Linux-Eingabesubsystems

Eingabe-Subsystem-Framework Das Linux-Eingabesubs...

Hinweise zum MySQL-Datenbank-Sicherungsprozess

Heute habe ich mir einige Dinge im Zusammenhang m...

Three.js-Beispielcode zur Implementierung des Tautropfen-Animationseffekts

Vorwort Hallo zusammen, hier ist der CSS-Assisten...

Spezifische Verwendung von Lazy Loading und Preloading in js

Verzögertes Laden (Lazy Loading) und Vorladen sin...