Warum reduzieren Sie (), um Nacheinander zu Lösen Verspricht Funktioniert

0
19

Schreiben von asynchronen JavaScript-ohne mithilfe des Promise-Objekt ist eine Menge, wie das Backen eines Kuchens mit geschlossenen Augen. Es kann getan werden, aber es wird chaotisch sein, und Sie werden wahrscheinlich am Ende brennt Sie sich.

Ich will nicht sagen, es ist notwendig, aber Sie bekommen die Idee. Es ist echt schön. Manchmal braucht es ein wenig helfen zu lösen einige einzigartige Herausforderungen, wie wenn Sie versuchen, um nacheinander lösen eine Reihe von Versprechen um, eine nach der anderen. Einen trick, wie das ist praktisch, zum Beispiel, wenn du tust, eine Art batch-Verarbeitung per AJAX. Sie wollen, dass der server-Prozess eine Reihe von Dingen, aber nicht alle auf einmal, so dass Sie Raum, der Verarbeitung sich im Laufe der Zeit.

Ausschluss-Pakete, die helfen, machen diese Aufgabe zu erleichtern (wie Caolan McMahon ‘ s async library), die am häufigsten vorgeschlagene Lösung für die sequenzielle Lösung verspricht, ist die Verwendung von Array.der Prototyp.verringern(). Sie vielleicht haben gehört dieser. Nehmen Sie eine Sammlung der Dinge, und reduzieren Sie zu einem einzigen Wert, so wie hier:

let result = [1,2,5].reduzieren((Akkumulator, item) => {
return Akku + item;
}, 0); // <– Unsere anfänglichen Wert.

console.log(result); // 8

Aber, bei der Verwendung von reduce() für unsere Zwecke, das setup sieht wie folgt aus.

lassen Sie Benutzer-IDS = [1,2,3];

Benutzerkennungen.reduzieren( (previousPromise, nextID) => {
zurück previousPromise.dann ist(() => {
zurück methodThatReturnsAPromise(nextID);
});
}, Versprechen.resolve());

Oder, in einem moderneren format:

lassen Sie Benutzer-IDS = [1,2,3];

Benutzerkennungen.reduzieren( async (previousPromise, nextID) => {
erwarten previousPromise;
zurück methodThatReturnsAPromise(nextID);
}, Versprechen.resolve());

Das ist ordentlich! Aber für die längste Zeit, die ich gerade verschlungen diese Lösung kopiert und das Stück code in meine Anwendung, weil es “funktioniert.” Dieser Beitrag ist mir ein stab zu verstehen, zwei Dinge:

  1. Warum ist dieser Ansatz auch funktionieren?
  2. Warum können wir nicht andere Array-Methoden, das gleiche zu tun?

Warum hat dies noch funktioniert?

Denken Sie daran, der Hauptzweck von reduce() mit “reduzieren” ein paar Dinge in eine Sache, und es macht, dass durch die Speicherung bis das Ergebnis in den Akkumulator, wie die Schleife läuft. Aber, der Akku muss nicht numerisch sein. Die Schleife zurückkehren kann, was es will (wie ein Versprechen), und zu recyceln, dass der Wert durch die callback-jeder iteration. Vor allem, egal was der Akku Wert ist, wird die Schleife selbst, die sich nie ändert sein Verhalten — einschließlich das Tempo der Ausführung. Er rollt einfach durch die Sammlung so schnell wie der thread erlaubt.

Das ist riesig zu verstehen, weil es geht wahrscheinlich gegen das, was Sie denken, ist passiert während dieser Schleife (zumindest hat es für mich). Wenn wir es verwenden, um nacheinander zu lösen verspricht, die reduce () – Schleife ist eigentlich nicht zu verlangsamen überhaupt. Es ist völlig synchron, zu tun, Ihre normale Sache so schnell wie möglich, eben wie immer.

Betrachten Sie den folgenden Codeausschnitt, und beachten Sie, wie die Fortschritte der Schleife nicht behindert überhaupt durch die Versprechungen wieder in den Rückruf.

Funktion methodThatReturnsAPromise(nextID) {
return new Promise((auflösen, ablehnen) => {
setTimeout(() => {

console.log(`Beheben! ${dayjs().format (“hh:mm:ss’)}`);

resolve();
}, 1000);
});
}

[1,2,3].reduzieren( (accumulatorPromise, nextID) => {

console.log(`Loop! ${dayjs().format (“hh:mm:ss’)}`);

zurück accumulatorPromise.dann ist(() => {
zurück methodThatReturnsAPromise(nextID);
});
}, Versprechen.resolve());

In der Konsole:

“Loop! 11:28:06”
“Loop! 11:28:06”
“Loop! 11:28:06”
“Lösen! 11:28:07”
“Lösen! 11:28:08”
“Lösen! 11:28:09”

Die Versprechungen lösen, um, wie wir erwarten, aber die Schleife selbst ist schnell, stabil und synchron. Nach einem Blick auf die MDN-polyfill für reduce(), macht das Sinn. Da ist nichts asynchron über eine while () – Schleife auslösen des callback() immer und immer wieder, das ist das, was passiert unter der Haube:

while (k < len) {
wenn (k, o) {
Wert = callback(Wert a[k], k, o);
}
k++;
}

Mit all dem im Verstand, die wirkliche Magie tritt in diesem Stück hier:

zurück previousPromise.dann ist(() => {
zurück methodThatReturnsAPromise(nextID)
});

Jedes mal, wenn Sie unsere callback feuert, kehren wir ein Versprechen, löst ein weiteres Versprechen. Und während verringern() nicht warten, bis eine Auflösung stattfindet, der Vorteil es bietet, ist die Fähigkeit zu übergeben, etwas wieder in die gleiche callback-nach jedem Lauf, eine Funktion, einzigartig zu verringern(). Als Ergebnis können wir eine Kette bilden Versprechungen lösen in mehr verspricht, macht alles schön und der Reihe nach:

neue Versprechen( (auflösen, ablehnen) => {
// Das Versprechen #1

resolve();
}).then( (result) => {
// Das Versprechen #2

return Ergebnis;
}).then( (result) => {
// Das Versprechen #3

return Ergebnis;
}); // … und so weiter!

All dies soll auch zeigen, warum können wir nicht einfach wieder einen einzelnen, neuen Versprechen in jeder iteration. Da die Schleife läuft synchron, jedes Versprechen wird gefeuert, sofort, anstatt zu warten, für diejenigen, die zuvor erstellt.

[1,2,3].reduzieren( (previousPromise, nextID) => {

console.log(`Loop! ${dayjs().format (“hh:mm:ss’)}`);

return new Promise((auflösen, ablehnen) => {
setTimeout(() => {
console.log(`Beheben! ${dayjs().format (“hh:mm:ss’)}`);
beheben(nextID);
}, 1000);
});
}, Versprechen.resolve());

In der Konsole:

“Loop! 11:31:20”
“Loop! 11:31:20”
“Loop! 11:31:20”
“Lösen! 11:31:21”
“Lösen! 11:31:21”
“Lösen! 11:31:21”

Ist es möglich, zu warten, bis die Verarbeitung beendet ist, bevor Sie etwas anderes tun? Ja. Die synchrone Natur von “reduce ()” bedeutet nicht, können Sie nicht werfen Sie eine Partei, die nach jedem Element vollständig verarbeitet wurde. Aussehen:

Funktion methodThatReturnsAPromise(id) {
return new Promise((auflösen, ablehnen) => {
setTimeout(() => {
console.log(`Verarbeitung ${id}`);
beheben(id);
}, 1000);
});
}

let result = [1,2,3].reduzieren( (accumulatorPromise, nextID) => {
zurück accumulatorPromise.dann ist(() => {
zurück methodThatReturnsAPromise(nextID);
});
}, Versprechen.resolve());

Ergebnis.dann ist(e => {
console.log(“Lösung ist komplett! Let ‘ s party.”)
});

Denn alle sind wir wieder in unseren callback ist eine verkettete Versprechen, dass alles, was wir bekommen, wenn die Schleife beendet ist: ein Versprechen. Danach können wir es behandeln allerdings wollen wir uns, auch lange nach der reduce() seinen Lauf genommen hat.

Warum nicht jedes andere Array-Methoden arbeiten?

Denken Sie daran, unter die Haube zu reduzieren(), wir warten nicht, für unseren Rückruf abgeschlossen ist, bevor Sie bewegen auf die nächste Position. Es ist völlig synchron. Das gleiche gilt für alle anderen Methoden:

  • Array.der Prototyp.anzeigen()
  • Array.der Prototyp.forEach()
  • Array.der Prototyp.filter()
  • Array.der Prototyp.einige()
  • Array.der Prototyp.jeder()

Aber reduce() ist etwas besonderes.

Wir haben festgestellt, dass der Grund, reduzieren() funktioniert für uns, da wir in der Lage sind, etwas zurückgeben rechts zurück zu unserem callback (nämlich ein Versprechen), das können wir dann aufbauen, indem man beheben, in anderen Versprechen. Mit all diesen Methoden, aber wir können einfach nicht passieren Sie ein argument zu unseren callback zurückgegeben wurde von unseren Rückruf. Statt, jede der callback-Argumente vorgegeben werden, so dass es unmöglich für uns zu nutzen für so etwas wie sequentielle Auflösung Versprechen.

[1,2,3].Karte((Element, [index, array]) => [Wert]);
[1,2,3].filter((Element, [index, array]) => [boolean]);
[1,2,3].einige((Element, [index, array]) => [boolean]);
[1,2,3].jeder((Element, [index, array]) => [boolean]);

Ich hoffe, das hilft!

Zumindest, ich hoffe, das hilft etwas Licht auf, warum reduce() ist einzigartig qualifiziert, um zu behandeln verspricht auf diese Weise, und vielleicht geben Ihnen ein besseres Verständnis von, wie die gemeinsamen Array-Methoden arbeiten unter der Haube. Hab ich was verpasst? Etwas falsch? Lassen Sie mich wissen!