Verarbeitet Node.js natives Promise.all parallel oder sequentiell?

Lesezeit: 10 Minuten

Verarbeitet Nodejs natives Promiseall parallel oder sequentiell
Yanik Rochon

Ich möchte diesen Punkt klarstellen, da die Dokumentation ist sich darüber nicht allzu klar;

Q1: Ist Promise.all(iterable) alle Promises sequentiell oder parallel abarbeiten? Oder, genauer gesagt, ist es das Äquivalent zu laufenden verketteten Versprechen wie

p1.then(p2).then(p3).then(p4).then(p5)....

oder ist es eine andere Art von Algorithmus, bei dem alle p1, p2, p3, p4, p5etc. werden gleichzeitig (parallel) aufgerufen und Ergebnisse werden zurückgegeben, sobald alle aufgelöst (oder einer verworfen)?

Q2: Wenn Promise.all Läuft parallel, gibt es eine bequeme Möglichkeit, eine Iteration sequenziell auszuführen?

Notiz: Ich möchte nicht Q oder Bluebird verwenden, sondern alle nativen ES6-Spezifikationen.

  • Fragen Sie nach der Knotenimplementierung (V8) oder nach der Spezifikation?

    – Amit

    13. Juni 2015 um 21:22 Uhr

  • Ich bin mir ziemlich sicher Promise.all führt sie parallel aus.

    – royhowie

    13. Juni 2015 um 21:23 Uhr

  • @Amit Ich habe es markiert node.js und io.js da ich es hier verwende. Also, ja, die V8-Implementierung, wenn Sie so wollen.

    – Yanik Rochon

    13. Juni 2015 um 21:24 Uhr

  • Versprechen können nicht “eingelöst” werden. Sie beginnen ihre Aufgabe, wenn sie sind erstellt – sie stellen nur die Ergebnisse dar – und Sie führen alles parallel aus, noch bevor sie an sie übergeben werden Promise.all.

    – Bergi

    13. Juni 2015 um 21:27 Uhr

  • Versprechen werden im Moment der Erstellung ausgeführt. (kann durch Ausführen eines Codes bestätigt werden). Im new Promise(a).then(b); c(); a wird zuerst ausgeführt, dann c, dann b. Es ist nicht Promise.all, das diese Versprechungen ausführt, sondern nur, wenn sie aufgelöst werden.

    – Mateon1

    13. Juni 2015 um 21:31 Uhr

1645704315 924 Verarbeitet Nodejs natives Promiseall parallel oder sequentiell
Bergi

Ist Promise.all(iterable) alle Versprechen einhalten?

Nein, Versprechen können nicht „eingelöst“ werden. Sie beginnen ihre Aufgabe, wenn sie sind erstellt – sie stellen nur die Ergebnisse dar – und Sie führen alles parallel aus, noch bevor sie an sie übergeben werden Promise.all.

Promise.all tut nur erwarten mehrere Versprechungen. Es spielt keine Rolle, in welcher Reihenfolge sie aufgelöst werden oder ob die Berechnungen parallel ausgeführt werden.

Gibt es eine bequeme Möglichkeit, eine Iteration sequenziell auszuführen?

Wenn Sie Ihre Versprechen bereits haben, können Sie nicht viel tun, aber Promise.all([p1, p2, p3, …]) (die keinen Begriff der Sequenz hat). Aber wenn Sie eine iterierbare asynchrone Funktion haben, können Sie sie tatsächlich nacheinander ausführen. Grundsätzlich müssen Sie aus

[fn1, fn2, fn3, …]

zu

fn1().then(fn2).then(fn3).then(…)

und die Lösung dafür ist die Verwendung Array::reduce:

iterable.reduce((p, fn) => p.then(fn), Promise.resolve())

  • Ist in diesem Beispiel ein Array der Funktionen iterierbar, die ein Versprechen zurückgeben, das Sie aufrufen möchten?

    – James Reategui

    15. Januar 2016 um 23:40 Uhr


  • @SSHThis: Es ist genau so then Sequenz – der Rückgabewert ist das Versprechen für die letzte fn Ergebnis, und Sie können andere Rückrufe damit verketten.

    – Bergi

    31. Mai 2016 um 21:58 Uhr


  • @wojjas Das ist genau gleichbedeutend mit fn1().then(p2).then(fn3).catch(…? Es ist nicht erforderlich, einen Funktionsausdruck zu verwenden.

    – Bergi

    1. Dezember 2016 um 0:07 Uhr

  • @wojjas Natürlich die retValFromF1 hineingegangen ist p2genau das ist es p2 tut. Sicher, wenn Sie mehr tun möchten (zusätzliche Variablen übergeben, mehrere Funktionen aufrufen usw.), müssen Sie einen Funktionsausdruck verwenden, obwohl Sie sich ändern p2 im Array wäre einfacher

    – Bergi

    1. Dezember 2016 um 11:22 Uhr

  • @robe007 Ja, das meinte ich iterable ist der [fn1, fn2, fn3, …] Reihe

    – Bergi

    7. August 2018 um 16:21 Uhr

Verarbeitet Nodejs natives Promiseall parallel oder sequentiell
david_adler

Parallel zu

await Promise.all(items.map(async (item) => { 
  await fetchItem(item) 
}))

Vorteile: Schneller. Alle Iterationen werden gestartet, auch wenn später eine fehlschlägt. Allerdings wird es “schnell scheitern”. Verwenden Promise.allSettledum alle Iterationen parallel abzuschließen, auch wenn einige fehlschlagen.

Der Reihe nach

for (const item of items) {
  await fetchItem(item)
}

Vorteile: Variablen in der Schleife können von jeder Iteration gemeinsam genutzt werden. Verhält sich wie normaler imperativer synchroner Code.

  • Oder: for (const item of items) await fetchItem(item);

    – Robert Penner

    24. Februar 2018 um 21:33 Uhr

  • @david_adler Im Parallelbeispiel hast du Vorteile gesagt Alle Iterationen werden ausgeführt, auch wenn eine fehlschlägt. Wenn ich mich nicht irre, würde dies immer noch schnell scheitern. Um dieses Verhalten zu ändern, kann man Folgendes tun: await Promise.all(items.map(async item => { return await fetchItem(item).catch(e => e) }))

    – Taimoor

    7. November 2018 um 9:33 Uhr


  • @Taimoor ja, es “schlägt schnell fehl” und führt die Ausführung von Code nach Promise.all fort, aber alle Iterationen werden weiterhin ausgeführt codepen.io/mfbx9da4/pen/BbaaXr

    – david_adler

    25. Februar 2019 um 17:12 Uhr

  • Dieser Ansatz ist besser, wenn die async Funktion ist ein API-Aufruf und Sie möchten den Server nicht mit DDOS belegen. Sie haben eine bessere Kontrolle über die einzelnen Ergebnisse und Fehler bei der Ausführung. Noch besser, Sie können entscheiden, welche Fehler fortgesetzt werden und welche die Schleife unterbrechen.

    – Mandarine

    11. Oktober 2019 um 13:20 Uhr

  • Beachten Sie, dass Javascript die asynchronen Anforderungen nicht tatsächlich “parallel” mit Threads ausführt, da Javascript Single-Threading ist. developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

    – david_adler

    18. Juni 2020 um 16:18 Uhr

1645704315 131 Verarbeitet Nodejs natives Promiseall parallel oder sequentiell
Adrian De Peretti

NodeJS führt Promises nicht parallel aus, es führt sie gleichzeitig aus, da es sich um eine Single-Threaded-Event-Loop-Architektur handelt. Es besteht die Möglichkeit, Dinge parallel auszuführen, indem ein neuer untergeordneter Prozess erstellt wird, um die Vorteile der Mehrkern-CPU zu nutzen.

Parallel vs. gleichzeitig

In der Tat, was Promise.all die Promises-Funktion in der entsprechenden Warteschlange stapeln (siehe Ereignisschleifenarchitektur), sie gleichzeitig ausführen (P1, P2 aufrufen, …), dann auf jedes Ergebnis warten und dann Promise.all mit allen Promises-Ergebnissen auflösen. Promise.all schlägt beim ersten fehlgeschlagenen Versprechen fehl, es sei denn, Sie müssen die Ablehnung selbst verwalten.

Es gibt einen großen Unterschied zwischen parallel und gleichzeitig, der erste führt eine andere Berechnung in einem separaten Prozess genau zur gleichen Zeit aus und sie werden in ihrem Rhythmus fortschreiten, während der andere die verschiedenen Berechnungen nacheinander ohne Wartezeit ausführt die vorherige Berechnung zur gleichen Zeit zu beenden und fortzusetzen, ohne voneinander abhängig zu sein.

Um Ihre Frage abschließend zu beantworten, Promise.all werden weder parallel noch sequentiell, sondern gleichzeitig ausgeführt.

  • Das ist nicht richtig. NodeJS kann Dinge parallel ausführen. NodeJS hat ein Worker-Thread-Konzept. Standardmäßig ist die Anzahl der Worker-Threads 4. Wenn Sie beispielsweise die Kryptobibliothek verwenden, um zwei Werte zu hashen, können Sie sie parallel ausführen. Zwei Worker-Threads werden die Aufgabe bearbeiten. Natürlich muss Ihre CPU mehrkernig sein, um Parallelität zu unterstützen.

    – Schihab

    11. Mai 2020 um 17:51 Uhr

  • Ja, du hast Recht, es ist das, was ich am Ende des ersten Absatzes gesagt habe, aber ich habe über Kinderprozesse gesprochen, natürlich können sie Arbeiter ausführen.

    – Adrian De Peretti

    11. Mai 2020 um 17:54 Uhr

  • Beste Antwort bisher. Ich war so verwirrt darüber, wie eine Single-Thread-Architektur wie Node.js mehrere Promises parallel ausführen kann. Vielen Dank Herr. PS Ich weiß, wie Worker-Threads sind und wie sie funktionieren, aber Versprechungen werden von der Node.js-Ereignisschleife selbst und nicht durch die Verwendung von libuv aufgelöst. Das Beste, was Node.js tun könnte, ist, sie (Promises) gleichzeitig auszuführen.

    – Muhammad Muzammil

    19. Januar 2021 um 6:25 Uhr

1645704316 314 Verarbeitet Nodejs natives Promiseall parallel oder sequentiell
tkarls

Bergis Antwort hat mich auf die richtige Spur gebracht Array.reduce.

Um jedoch tatsächlich die Funktionen zu erhalten, die meine Versprechen zurückgeben, um sie nacheinander auszuführen, musste ich noch mehr Verschachtelungen hinzufügen.

Mein eigentlicher Anwendungsfall ist eine Reihe von Dateien, die ich aufgrund nachgelagerter Einschränkungen nacheinander übertragen muss …

Hier ist, was ich am Ende hatte:

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(() => {
            return transferFile(theFile); //function returns a promise
        });
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

Wie vorherige Antworten vermuten lassen, verwenden Sie:

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(transferFile(theFile));
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});

Ich habe nicht gewartet, bis die Übertragung abgeschlossen ist, bevor ich eine weitere gestartet habe, und auch der Text “Alle Dateien übertragen” wurde angezeigt, bevor die erste Dateiübertragung gestartet wurde.

Ich bin mir nicht sicher, was ich falsch gemacht habe, wollte aber mitteilen, was für mich funktioniert hat.

Edit: Seit ich diesen Beitrag geschrieben habe, verstehe ich jetzt, warum die erste Version nicht funktioniert hat. then() erwartet ein Funktion ein Versprechen zurückgeben. Sie sollten also den Funktionsnamen ohne Klammern übergeben! Jetzt will meine Funktion ein Argument, also muss ich mich in eine anonyme Funktion einschließen, die kein Argument hat!

1645704316 122 Verarbeitet Nodejs natives Promiseall parallel oder sequentiell
Kennzeichen

Sie können eine Iterable auch sequenziell mit einer asynchronen Funktion unter Verwendung einer rekursiven Funktion verarbeiten. Zum Beispiel ein Array gegeben a mit asynchroner Funktion verarbeiten someAsyncFunction():

var a = [1, 2, 3, 4, 5, 6]

function someAsyncFunction(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("someAsyncFunction: ", n)
      resolve(n)
    }, Math.random() * 1500)
  })
}

//You can run each array sequentially with: 

function sequential(arr, index = 0) {
  if (index >= arr.length) return Promise.resolve()
  return someAsyncFunction(arr[index])
    .then(r => {
      console.log("got value: ", r)
      return sequential(arr, index + 1)
    })
}

sequential(a).then(() => console.log("done"))

  • verwenden array.prototype.reduce ist in Bezug auf die Leistung viel besser als eine rekursive Funktion

    – Mateusz Sowinski

    18. Juli 2018 um 15:32 Uhr

  • @MateuszSowiński, es gibt eine Zeitüberschreitung von 1500 ms zwischen jedem Anruf. Wenn man bedenkt, dass dies asynchrone Aufrufe nacheinander durchführt, ist es schwer zu erkennen, wie relevant dies selbst für eine sehr schnelle asynchrone Abwicklung ist.

    – Kennzeichen

    18. Juli 2018 um 15:44 Uhr

  • Nehmen wir an, Sie müssen 40 wirklich schnelle asynchrone Funktionen nacheinander ausführen – die Verwendung rekursiver Funktionen würde Ihren Speicher ziemlich schnell verstopfen

    – Mateusz Sowinski

    18. Juli 2018 um 15:46 Uhr

  • @MateuszSowiński, dass der Stapel hier nicht landet … wir kehren nach jedem Aufruf zurück. Vergleichen Sie das mit reduce wo man das ganze aufbauen muss then() in einem Schritt verketten und dann ausführen.

    – Kennzeichen

    18. Juli 2018 um 15:56 Uhr


  • Beim 40. Aufruf der sequentiellen Funktion befindet sich der erste Aufruf der Funktion noch im Speicher und wartet auf die Rückkehr der Kette sequentieller Funktionen

    – Mateusz Sowinski

    18. Juli 2018 um 16:00 Uhr

1641742403 626 Wie kann ich Text gleichmasig uber mehrere Zeilen verteilen
Tomerikoo

Nur um auf die Antwort von @Bergi einzugehen (die sehr prägnant, aber schwer zu verstehen ist;)

Dieser Code führt jedes Element im Array aus und fügt die nächste ‘then-Kette’ am Ende hinzu:

function eachorder(prev,order) {
        return prev.then(function() {
          return get_order(order)
            .then(check_order)
            .then(update_order);
        });
    }
orderArray.reduce(eachorder,Promise.resolve());

  • verwenden array.prototype.reduce ist in Bezug auf die Leistung viel besser als eine rekursive Funktion

    – Mateusz Sowinski

    18. Juli 2018 um 15:32 Uhr

  • @MateuszSowiński, es gibt eine Zeitüberschreitung von 1500 ms zwischen jedem Anruf. Wenn man bedenkt, dass dies asynchrone Aufrufe nacheinander durchführt, ist es schwer zu erkennen, wie relevant dies selbst für eine sehr schnelle asynchrone Abwicklung ist.

    – Kennzeichen

    18. Juli 2018 um 15:44 Uhr

  • Nehmen wir an, Sie müssen 40 wirklich schnelle asynchrone Funktionen nacheinander ausführen – die Verwendung rekursiver Funktionen würde Ihren Speicher ziemlich schnell verstopfen

    – Mateusz Sowinski

    18. Juli 2018 um 15:46 Uhr

  • @MateuszSowiński, dass der Stapel hier nicht landet … wir kehren nach jedem Aufruf zurück. Vergleichen Sie das mit reduce wo man das ganze aufbauen muss then() in einem Schritt verketten und dann ausführen.

    – Kennzeichen

    18. Juli 2018 um 15:56 Uhr


  • Beim 40. Aufruf der sequentiellen Funktion befindet sich der erste Aufruf der Funktion noch im Speicher und wartet auf die Rückkehr der Kette sequentieller Funktionen

    – Mateusz Sowinski

    18. Juli 2018 um 16:00 Uhr

Verwenden asynchron warten Eine Reihe von Promises kann einfach nacheinander ausgeführt werden:

let a = [promise1, promise2, promise3];

async function func() {
  for(let i=0; i<a.length; i++){
    await a[i]();
  }  
}

func();

Hinweis: Wenn in der obigen Implementierung ein Promise abgelehnt wird, wird der Rest nicht ausgeführt. Wenn Sie möchten, dass alle Ihre Promises ausgeführt werden, wickeln Sie Ihr Promise ein await a[i](); Innerhalb try catch

842940cookie-checkVerarbeitet Node.js natives Promise.all parallel oder sequentiell?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy