Betrachten Sie den folgenden Code, der ein Array von Dateien seriell / sequentiell liest. readFiles
gibt ein Versprechen zurück, das erst aufgelöst wird, wenn alle Dateien nacheinander gelesen wurden.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) =>
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start!
});
};
Der obige Code funktioniert, aber ich mag es nicht, eine Rekursion durchführen zu müssen, damit die Dinge nacheinander ablaufen. Gibt es eine einfachere Möglichkeit, diesen Code neu zu schreiben, damit ich meine seltsame readSequential
Funktion nicht verwenden muss?
Ursprünglich habe ich versucht , zu verwenden Promise.all
, sondern dass alle der verursacht readFile
Anrufe gleichzeitig passieren, das ist nicht , was ich will:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
javascript
promise
q
sequential
serial-processing
XåpplI'-I0llwlg'I -
quelle
quelle
readFileSequential()
ist bereits zurückgekehrt, bevor der nächste aufgerufen wird (da er asynchron ist, wird er abgeschlossen, lange nachdem der ursprüngliche Funktionsaufruf bereits zurückgegeben wurde).Antworten:
Update 2017 : Ich würde eine asynchrone Funktion verwenden, wenn die Umgebung dies unterstützt:
Wenn Sie möchten, können Sie das Lesen der Dateien verschieben, bis Sie sie mithilfe eines Async-Generators benötigen (sofern Ihre Umgebung dies unterstützt):
Update: Im zweiten Gedanken - ich könnte stattdessen eine for-Schleife verwenden:
Oder kompakter mit reduzieren:
In anderen Versprechensbibliotheken (wie when und Bluebird) haben Sie Dienstprogrammmethoden dafür.
Zum Beispiel wäre Bluebird:
Obwohl es wirklich keinen Grund gibt , Async nicht zu verwenden, warten Sie heute.
quelle
Promise.resolve(Promise.resolve(15))
ist identisch mitPromise.resolve(15)
.So führe ich Aufgaben lieber in Serie aus.
quelle
result = result.then(task);
Diese Frage ist alt, aber wir leben in einer Welt mit ES6 und funktionalem JavaScript. Lassen Sie uns sehen, wie wir uns verbessern können.
Da Versprechen sofort ausgeführt werden, können wir nicht einfach eine Reihe von Versprechen erstellen, sie werden alle parallel ausgelöst.
Stattdessen müssen wir eine Reihe von Funktionen erstellen, die ein Versprechen zurückgeben. Jede Funktion wird dann nacheinander ausgeführt, wodurch das Versprechen im Inneren gestartet wird.
Wir können dies auf einige Arten lösen, aber meine Lieblingsmethode ist die Verwendung
reduce
.reduce
In Kombination mit Versprechungen wird es etwas schwierig, es zu verwenden , deshalb habe ich den einen Liner unten in einige kleinere verdauliche Bissen zerlegt.Die Essenz dieser Funktion besteht darin,
reduce
mit einem Anfangswert vonPromise.resolve([])
oder einem Versprechen zu beginnen, das ein leeres Array enthält.Dieses Versprechen wird dann als an die
reduce
Methode weitergegebenpromise
. Dies ist der Schlüssel, um jedes Versprechen nacheinander zu verketten. Das nächste Versprechen, das ausgeführt werden muss, ist,func
und wenn diethen
Brände ausgelöst werden, werden die Ergebnisse verkettet, und dieses Versprechen wird dann zurückgegeben, wobei das ausgeführt wirdreduce
Zyklus mit der nächsten Versprechensfunktion ausgeführt wird.Sobald alle Versprechen ausgeführt wurden, enthält das zurückgegebene Versprechen eine Reihe aller Ergebnisse jedes Versprechens.
ES6 Beispiel (ein Liner)
ES6 Beispiel (aufgeschlüsselt)
Verwendungszweck:
quelle
Array.prototype.concat.bind(result)
ist der Teil, den ich vermisst habe, musste manuell auf Ergebnisseconsole.log.bind(console)
Aussage in Ihrem letzten Beispiel jetzt normalerweise unnötig ist. In diesen Tagen können Sie einfach passierenconsole.log
. Z.B.serial(funcs).then(console.log)
. Getestet auf aktuellen NodeJS und Chrome.Promise.resolve([]).then((x) => { const data = mockApi('/data/1'); return Promise.resolve(x.concat(data)) }).then((x) => { const data = mockApi('/data/2'); return Promise.resolve(x.concat(data)); });
Promise.resolve([]).then(x => someApiCall('url1').then(r => x.concat(r))).then(x => someApiCall('url2').then(r => x.concat(r)))
und so weiterUm dies einfach in ES6 zu tun:
quelle
files.forEach
ob Dateien ein Array sind.for (file of files) {...}
.Promise.resolve()
, um ein bereits gelöstes Versprechen im wirklichen Leben zu erstellen. Warum nicht?Promise.resolve()
scheint sauberer alsnew Promise(success => success())
.Promise.resolve();
in Ihrem Code verwenden.return sequence;
setzensequence.then(() => { do stuff });
Einfache Verwendung für das Versprechen von Standard Node.j:
AKTUALISIEREN
items-versprechen ist ein gebrauchsfertiges NPM-Paket, das dasselbe tut.
quelle
Ich musste viele sequentielle Aufgaben ausführen und habe diese Antworten verwendet, um eine Funktion zu erstellen, die sich um die Bearbeitung jeder sequentiellen Aufgabe kümmert ...
Die Funktion akzeptiert 2 Argumente + 1 optional. Das erste Argument ist das Array, an dem wir arbeiten werden. Das zweite Argument ist die Aufgabe selbst, eine Funktion, die ein Versprechen zurückgibt. Die nächste Aufgabe wird erst gestartet, wenn dieses Versprechen aufgelöst wird. Das dritte Argument ist ein Rückruf, der ausgeführt wird, wenn alle Aufgaben erledigt wurden. Wenn kein Rückruf übergeben wird, gibt die Funktion das von ihr erstellte Versprechen zurück, damit wir das Ende bewältigen können.
Hier ist ein Anwendungsbeispiel:
Hoffe es spart jemandem etwas Zeit ...
quelle
Die schönste Lösung, die ich herausfinden konnte, war mit
bluebird
Versprechungen. Sie können einfach tun,Promise.resolve(files).each(fs.readFileAsync);
was garantiert, dass Versprechen nacheinander gelöst werden.quelle
Promise.each(filtes, fs.readFileAsync)
. Übrigens, musst du das nicht tun.bind(fs)
?new Array(int)
. Sie müssen lediglich daslength
Schlüssel-Wert-Paar voreingestellt haben, was sich darauf auswirkt, wie viele Indizes während der längenbasierten Iteration verwendet werden. Es hat Null Auswirkung auf die Indexierung oderDies ist eine geringfügige Abweichung von einer anderen Antwort oben. Native Versprechen verwenden:
Erläuterung
Wenn Sie diese Aufgaben haben
[t1, t2, t3]
, entspricht das oben GesagtePromise.resolve().then(t1).then(t2).then(t3)
. Es ist das Verhalten von reduzieren.Wie benutzt man
Zuerst müssen Sie eine Liste von Aufgaben erstellen! Eine Aufgabe ist eine Funktion, die kein Argument akzeptiert. Wenn Sie Argumente an Ihre Funktion übergeben müssen, verwenden Sie
bind
oder andere Methoden, um eine Aufgabe zu erstellen. Zum Beispiel:quelle
Meine bevorzugte Lösung:
Es unterscheidet sich nicht grundlegend von anderen hier veröffentlichten, aber:
Anwendungsbeispiel:
Getestet auf angemessenem aktuellem Chrome (v59) und NodeJS (v8.1.2).
quelle
Verwenden Sie
Array.prototype.reduce
und denken Sie daran, Ihre Versprechen in eine Funktion zu verpacken, da sie sonst bereits ausgeführt werden!nett und einfach ... Sie sollten in der Lage sein, den gleichen Samen für die Leistung usw. wiederzuverwenden.
Es ist wichtig, sich vor leeren Arrays oder Arrays mit nur 1 Element zu schützen, wenn Sie Reduzieren verwenden. Daher ist diese Technik die beste Wahl:
und dann nenne es wie:
quelle
Ich habe diese einfache Methode für das Promise-Objekt erstellt:
Erstellen Sie eine Promise.sequence-Methode und fügen Sie sie dem Promise-Objekt hinzu
Verwendungszweck:
Das Beste an dieser Erweiterung des Promise-Objekts ist, dass sie mit dem Stil der Versprechen übereinstimmt. Promise.all und Promise.sequence werden auf dieselbe Weise aufgerufen, haben jedoch unterschiedliche Semantiken.
Vorsicht
Das sequentielle Ausführen von Versprechungen ist normalerweise keine sehr gute Möglichkeit, Versprechungen zu verwenden. Normalerweise ist es besser, Promise.all zu verwenden und den Browser den Code so schnell wie möglich ausführen zu lassen. Es gibt jedoch echte Anwendungsfälle dafür - zum Beispiel beim Schreiben einer mobilen App mit Javascript.
quelle
Promise.all
und IhrePromise.sequence
. Einer nimmt eine Reihe von Versprechungen, der andere eine Reihe von Funktionen, die Versprechungen zurückgeben.reduce
ähnliches wie in Benjamins Antwort viel einfacher.Sie können diese Funktion verwenden, mit der die Liste der versprochenen Fakten abgerufen wird:
Promise Factory ist nur eine einfache Funktion, die ein Versprechen zurückgibt:
Es funktioniert, weil eine Versprechensfabrik das Versprechen erst erstellt, wenn sie dazu aufgefordert wird. Es funktioniert genauso wie eine damalige Funktion - tatsächlich ist es dasselbe!
Sie möchten überhaupt nicht über eine Reihe von Versprechungen hinweggehen. Gemäß der Versprechen-Spezifikation beginnt die Ausführung eines Versprechens, sobald es erstellt wurde. Was Sie also wirklich wollen, ist eine Reihe von Versprechungsfabriken ...
Wenn Sie mehr über Versprechen erfahren möchten, sollten Sie diesen Link überprüfen: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
quelle
Meine Antwort basiert auf https://stackoverflow.com/a/31070150/7542429 .
Diese Lösung gibt die Ergebnisse als Array wie Promise.all () zurück.
Verwendungszweck:
quelle
Die Antwort von @ joelnet hat mir sehr gut gefallen, aber für mich ist diese Art der Codierung etwas schwer zu verdauen. Deshalb habe ich ein paar Tage lang versucht, herauszufinden, wie ich dieselbe Lösung besser lesbar ausdrücken kann, und das ist meine nehmen, nur mit einer anderen Syntax und einigen Kommentaren.
quelle
Wie Bergi bemerkte, denke ich, dass die beste und klarste Lösung die Verwendung von BlueBird.each ist, Code unten:
quelle
Zunächst müssen Sie verstehen, dass ein Versprechen zum Zeitpunkt der Erstellung ausgeführt wird.
Also zum Beispiel, wenn Sie einen Code haben:
Sie müssen es ändern in:
Dann müssen wir Versprechen nacheinander verketten:
Durch Ausführen
after()
wird sichergestellt, dass das Versprechen nur dann erstellt (und ausgeführt) wird, wenn es soweit ist.quelle
Ich verwende den folgenden Code, um das Promise-Objekt zu erweitern. Es behandelt die Ablehnung der Versprechen und gibt eine Reihe von Ergebnissen zurück
Code
Beispiel
quelle
Wenn Sie möchten, können Sie Reduzieren verwenden, um ein sequentielles Versprechen abzugeben, zum Beispiel:
es wird immer nacheinander funktionieren.
quelle
Mit modernem ES:
quelle
Mit Async / Await (wenn Sie die Unterstützung von ES7 haben)
(Sie müssen
for
loop verwenden und nicht,forEach
weil async / await Probleme beim Ausführen einer forEach-Schleife hat.)Ohne Async / Warten (mit Promise)
quelle
forEach
(gemäß dieser ) zu arbeitenAnhand des Titels der Frage "Versprechen nacheinander auflösen (dh nacheinander)?" Könnten wir verstehen, dass das OP mehr an der sequentiellen Behandlung von Versprechungen bei der Abwicklung als an sequentiellen Anrufen an sich interessiert ist .
Diese Antwort wird angeboten:
Wenn gleichzeitige Anrufe wirklich nicht erwünscht sind, lesen Sie die Antwort von Benjamin Gruenbaum, in der sequentielle Anrufe (usw.) umfassend behandelt werden.
Wenn Sie jedoch (für eine verbesserte Leistung) an Mustern interessiert sind, die gleichzeitige Anrufe und anschließende sequentielle Bearbeitung von Antworten ermöglichen, lesen Sie bitte weiter.
Es ist verlockend zu glauben, dass Sie
Promise.all(arr.map(fn)).then(fn)
(wie ich es schon oft getan habe) oder den ausgefallenen Zucker einer Promise lib (insbesondere den von Bluebird) verwenden müssen, aber (mit Anerkennung dieses Artikels ) einarr.map(fn).reduce(fn)
Muster wird die Arbeit erledigen, mit den Vorteilen, dass es:.then()
verwendet.Hier ist es geschrieben für
Q
.Hinweis: Nur dieses eine Fragment
Q()
ist spezifisch für Q. Für jQuery müssen Sie sicherstellen, dass readFile () ein jQuery-Versprechen zurückgibt. Mit A + libs werden ausländische Versprechen aufgenommen.Der Schlüssel hier ist das
sequence
Versprechen der Reduktion , das den Umgang mit denreadFile
Versprechen, aber nicht deren Erstellung , in eine Reihenfolge bringt.Und wenn Sie das einmal aufgenommen haben, ist es vielleicht etwas umwerfend, wenn Sie feststellen, dass die
.map()
Bühne eigentlich nicht notwendig ist! Der gesamte Auftrag, parallele Anrufe plus serielle Bearbeitung in der richtigen Reihenfolge, kannreduce()
allein erledigt werden, plus dem zusätzlichen Vorteil einer weiteren Flexibilität für:Hier ist es
Q
wieder.Das ist das Grundmuster. Wenn Sie dem Anrufer auch Daten (z. B. die Dateien oder eine Transformation davon) liefern möchten, benötigen Sie eine milde Variante.
quelle
sequence.then(() => filePromise)
Ding ist ein Antimuster - es verbreitet Fehler nicht so schnell wie möglich (und erstelltunhandledRejection
in Bibliotheken, die sie unterstützen). Sie sollten lieberQ.all([sequence, filePromise])
oder verwenden$.when(sequence, filePromise)
. Zugegeben, dieses Verhalten könnte das sein, was Sie wollen, wenn Sie Fehler ignorieren oder überspringen möchten, aber Sie sollten dies zumindest als Nachteil erwähnen.unhandledRejection
Ereignissen führt. In Bluebird können Sie dies umgehen, indem Siesequence.return(filePromise)
das gleiche Verhalten verwenden, aber Ablehnungen problemlos verarbeiten. Ich kenne keine Referenz, ich habe sie mir gerade ausgedacht - ich glaube, das "(Anti) Muster" hat noch keinen Namen.Ihr Ansatz ist nicht schlecht, hat jedoch zwei Probleme: Er verschluckt Fehler und verwendet das Explicit Promise Construction Antipattern.
Sie können beide Probleme lösen und den Code sauberer gestalten, während Sie immer noch dieselbe allgemeine Strategie anwenden:
quelle
Wenn eine andere Person bei der Ausführung von CRUD-Operationen eine garantierte Methode zur strikt sequentiellen Lösung von Versprechungen benötigt, können Sie auch den folgenden Code als Grundlage verwenden.
Solange Sie 'return' hinzufügen, bevor Sie jede Funktion aufrufen, ein Versprechen beschreiben und dieses Beispiel als Grundlage verwenden, wird der nächste Funktionsaufruf .then () nach Abschluss des vorherigen konsequent gestartet:
quelle
Die Array-Push- und Pop-Methode kann für die Abfolge von Versprechungen verwendet werden. Sie können auch neue Versprechen abgeben, wenn Sie zusätzliche Daten benötigen. Dies ist der Code, den ich in React Infinite Loader verwenden werde, um eine Seitenfolge zu laden.
quelle
Die meisten Antworten enthalten nicht die Ergebnisse ALLER Versprechen einzeln. Wenn also jemand nach diesem bestimmten Verhalten sucht, ist dies eine mögliche Lösung mit Rekursion.
Es folgt dem Stil von
Promise.all
:Gibt das Ergebnisarray im
.then()
Rückruf zurück.Wenn ein Versprechen fehlschlägt, wird es sofort im
.catch()
Rückruf zurückgegeben.Hinweis zur
tasks
Array-Deklaration :In diesem Fall ist es nicht möglich, die folgende Notation zu verwenden, wie
Promise.all
sie verwendet werden würde:Und wir müssen verwenden:
Der Grund dafür ist, dass JavaScript das Versprechen sofort nach seiner Deklaration ausführt. Wenn wir Methoden wie verwenden
Promise.all
, wird nur überprüft, ob der Status aller von ihnen istfulfilled
oderrejected
, aber die Exektion selbst wird nicht gestartet. Mit() => promise()
stoppen wir die Ausführung, bis sie aufgerufen wird.quelle
Hier ist der Schlüssel, wie Sie die Schlaffunktion aufrufen. Sie müssen ein Array von Funktionen übergeben, das selbst ein Versprechen anstelle eines Arrays von Versprechen zurückgibt.
quelle
Dies soll erweitern, wie eine Folge von Versprechungen allgemeiner verarbeitet werden kann, wobei dynamische / unendliche Folgen unterstützt werden, die auf der Implementierung von spex.sequence basieren :
Diese Lösung funktioniert nicht nur mit Sequenzen beliebiger Größe, sondern Sie können auch problemlos Datenbeschränkung und Lastausgleich hinzufügen .
quelle