wake-up-neo.com

Node.js native Promise.all-Verarbeitung parallel oder sequentiell?

Ich möchte diesen Punkt klarstellen, da die Dokumentation nicht zu klar ist;

Q1: Werden alle Versprechen von Promise.all(iterable) sequentiell oder parallel verarbeitet? Oder, genauer gesagt, ist es das Äquivalent zu laufenden verketteten Versprechen 

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

oder ist es eine andere Art von Algorithmus, bei der alle p1, p2, p3, p4, p5 usw. zur gleichen Zeit (parallel) aufgerufen werden und die Ergebnisse zurückgegeben werden, sobald alle aufgelöst (oder abgelehnt werden)?

Q2: Wenn Promise.all parallel ausgeführt wird, gibt es eine praktische Möglichkeit, eine iterierbare Sequenz sequentiell auszuführen?

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

108
Yanick Rochon

Führt Promise.all(iterable) alle Versprechen ein?

Nein, Versprechen können nicht "ausgeführt werden". Sie beginnen ihre Aufgabe, wenn sie erstellt werden - sie repräsentieren nur die Ergebnisse - und you führen alles parallel aus, sogar bevor sie an Promise.all übergeben werden.

Promise.all macht nur await mehrere Zusagen. Es ist egal, in welcher Reihenfolge sie aufgelöst werden oder ob die Berechnungen parallel ausgeführt werden.

gibt es eine bequeme Möglichkeit, eine iterierbare Sequenz sequentiell auszuführen?

Wenn Sie bereits Ihre Versprechen haben, können Sie nicht viel tun, außer Promise.all([p1, p2, p3, …]) (die keine Vorstellung von Sequenz hat). Wenn Sie jedoch mit asynchronen Funktionen iterierbar sind, können Sie sie tatsächlich nacheinander ausführen. Grundsätzlich musst du auskommen 

[fn1, fn2, fn3, …]

zu

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

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

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

Parallel zu

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

Vorteile: Schneller. Alle Iterationen werden auch ausgeführt, wenn einer fehlschlägt.

Der Reihe nach

for (let i = 0; i < items.length; i++) {
    await fetchItem(items[i])
}

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

30
david_adler

Bergis Antwort brachte mich mit Array.reduce auf den richtigen Weg.

Um die Funktionen tatsächlich zu erhalten und mein Versprechen zurückzugeben, eines nach dem anderen auszuführen, musste ich noch etwas verschachteln.

Mein echter Anwendungsfall ist ein Array von Dateien, die ich aufgrund der nachgelagerten Grenzen nacheinander übertragen muss ...

Hier ist was ich endete.

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 in früheren Antworten vorgeschlagen, 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);
});

warten Sie nicht, bis die Übertragung abgeschlossen ist, bevor Sie eine andere starten, und auch der Text "Alle Dateien übertragen" kam vor dem Start der ersten Dateiübertragung.

Ich weiß nicht, 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 funktionierte. then () erwartet eine Funktion Rückgabe eines Versprechens. Sie sollten also den Funktionsnamen ohne Klammern übergeben! Nun, meine Funktion will ein Argument, also muss ich mich in eine anonyme Funktion einarbeiten, ohne Argumente!

9
tkarls

Sie können eine iterierbare Funktion auch mit einer asynchronen Funktion mit einer rekursiven Funktion sequentiell verarbeiten. Angenommen, ein Array a soll mit der asynchronen Funktion someAsyncFunction() verarbeitet werden: 

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"))

3
Mark Meyer

um nur auf @ Bergis Antwort einzugehen (was sehr kurz ist, aber schwierig zu verstehen ist;)

Dieser Code führt jedes Element im Array aus und fügt die nächste "dann-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());

hoffe das macht Sinn.

3
TimoSolo

dies könnte einen Teil Ihrer Frage beantworten.

ja, Sie können ein Array von Versprechungen, die Funktionen zurückgeben, wie folgt verketten ... (dies übergibt das Ergebnis jeder Funktion an die nächste). Sie können es natürlich bearbeiten, um dasselbe Argument (oder keine Argumente) an jede Funktion zu übergeben.

function tester1(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a + 1);
    }, 1000);
  })
}

function tester2(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a * 5);
    }, 1000);
  })
}

function promise_chain(args, list, results) {

  return new Promise(function(done, errs) {
    var fn = list.shift();
    if (results === undefined) results = [];
    if (typeof fn === 'function') {
      fn(args).then(function(result) {
        results.Push(result);
        console.log(result);
        promise_chain(result, list, results).then(done);
      }, errs);
    } else {
      done(results);
    }

  });

}

promise_chain(0, [tester1, tester2, tester1, tester2, tester2]).then(console.log.bind(console), console.error.bind(console));

0
cestmoi

Ich habe für verwendet, um sequentielle Versprechen zu lösen. Ich bin mir nicht sicher, ob es hier hilft, aber das habe ich getan.

async function run() {
    for (let val of arr) {
        const res = await someQuery(val)
        console.log(val)
    }
}

run().then().catch()
0
Nick Kotenberg

Mit async await kann eine Reihe von Versprechungen 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 Versprechen abgelehnt wird, wird der Rest nicht ausgeführt. Wenn Sie möchten, dass alle Ihre Versprechen ausgeführt werden, verpacken Sie await a[i](); in try catch

0
Ayan

Sie können es durch for-Schleife tun.

async-Funktion gibt Versprechen zurück

async function createClient(client) {
    return await Client.create(client);
}

let clients = [client1, client2, client3];

wenn Sie folgenden Code schreiben, werden die Clients parallel erstellt

const createdClientsArray = yield Promise.all(clients.map((client) =>
    createClient(client);
));

dann werden alle Kunden parallel angelegt. Wenn Sie jedoch einen Client sequentiell erstellen möchten, sollten Sie for-Schleife verwenden

const createdClientsArray = [];
for(let i = 0; i < clients.length; i++) {
    const createdClient = yield createClient(clients[i]);
    createdClientsArray.Push(createdClient);
}

dann werden alle Kunden sequentiell erstellt.

glückliche Codierung :)

0
Deepak Sisodiya

Bergis Antwort hat mir geholfen, den Aufruf synchron zu machen. Ich habe unten ein Beispiel hinzugefügt, in dem wir jede Funktion aufrufen, nachdem die vorherige Funktion aufgerufen wurde.

function func1 (param1) {
    console.log("function1 : " + param1);
}
function func2 () {
    console.log("function2");
}
function func3 (param2, param3) {
    console.log("function3 : " + param2 + ", " + param3);
}

function func4 (param4) {
    console.log("function4 : " + param4);
}
param4 = "Kate";

//adding 3 functions to array

a=[
    ()=>func1("Hi"),
    ()=>func2(),
    ()=>func3("Lindsay",param4)
  ];

//adding 4th function

a.Push(()=>func4("dad"));

//below does func1().then(func2).then(func3).then(func4)

a.reduce((p, fn) => p.then(fn), Promise.resolve());
0
Nithi