Waarom het Gebruik te verminderen() om Opeenvolgend Lossen Belooft Werkt

0
23

Het schrijven van asynchrone JavaScript zonder de Belofte object is een partij als het bakken van een taart met uw ogen gesloten. Het kan worden gedaan, maar het zal rommelig en u zult waarschijnlijk uiteindelijk het branden zelf.

Ik zal niet zeggen dat het nodig is, maar je krijgt het idee. Het is echt leuk. Soms, hoewel, het moet een beetje helpen bij het oplossen van een aantal unieke uitdagingen, zoals wanneer je probeert om opeenvolgend het oplossen van een bos van beloften om de ene na de andere. Een truc zoals dit is handig, bijvoorbeeld wanneer je bezig bent een soort van batch-verwerking via AJAX. Wilt u de server voor het verwerken van een heleboel dingen, maar niet allemaal tegelijk, zodat u ruimte de verwerking van de loop van de tijd.

Uitspraak pakketten die helpen om deze taak te vereenvoudigen (zoals Caolan McMahon ‘ s async bibliotheek), de meest gebruikte oplossing voor achtereenvolgens het oplossen van beloften is het gebruik van Array.prototype.het verminderen van(). Je zou kunnen hebben gehoord van deze. Een verzameling van dingen, en deze te reduceren tot een enkele waarde, zoals deze:

laten resultaat = [1,2,5].verminderen((accumulator, item) => {
terug accu + item;
}, 0); // <– Onze initiële waarde.

console.log(result); // 8

Maar, bij gebruik van het verminderen van (zijn) voor ons doel, de setup ziet er meer uit als dit:

laat userIDs = [1,2,3];

userIDs.verminderen( (previousPromise, nextID) => {
terug previousPromise.dan(() => {
terug methodThatReturnsAPromise(nextID);
});
}, Belofte.het oplossen van());

Of, in een meer moderne indeling:

laat userIDs = [1,2,3];

userIDs.het verminderen van( asynchroon (previousPromise, nextID) => {
wachten previousPromise;
terug methodThatReturnsAPromise(nextID);
}, Belofte.het oplossen van());

Dit is mooi! Maar voor de langste tijd, ik slikte deze oplossing en gekopieerd dat stuk code in mijn toepassing, omdat het “werken”. Dit post ik een steek in het begrijpen van de twee dingen:

  1. Waarom werkt deze aanpak ook werken?
  2. Waarom kunnen we niet gebruik maken van andere Array methoden om hetzelfde te doen?

Waarom gaat dit ook werken?

Vergeet niet, het belangrijkste doel van het verminderen van() is te “verminderen” een heleboel dingen in één ding, en het doet dat door het opslaan van het resultaat in de accu als de lus wordt uitgevoerd. Maar die accu hoeft niet numeriek. De lus terug wat het wil (een belofte), en het hergebruiken waarde via de callback elke iteratie. Met name, ongeacht wat de accumulator waarde is, de loop zelf nooit verandert zijn gedrag — met inbegrip van het tempo van uitvoering. Het blijft gewoon rollend door de collectie zo snel als de draad staat.

Dit is enorm belangrijk om te begrijpen, want het gaat waarschijnlijk tegen wat denk je dat er gebeurt tijdens deze lus (tenminste, niet voor mij). Als we het gebruiken om opeenvolgend oplossen van beloften, het verminderen () – lus is eigenlijk niet te vertragen. Het is volledig synchroon lopen, het doen van zijn normale ding zo snel als het kan, net als altijd.

Kijk naar het volgende fragment en zien hoe de voortgang van de lus niet gehinderd door de beloften terug in de callback.

functie methodThatReturnsAPromise(nextID) {
terug nieuwe Belofte((lossen, weigeren) => {
setTimeout(() => {

console.log(`Oplossen! ${dayjs().de indeling (hh:mm:ss’)}`);

het oplossen van();
}, 1000);
});
}

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

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

terug accumulatorPromise.dan(() => {
terug methodThatReturnsAPromise(nextID);
});
}, Belofte.het oplossen van());

In onze console:

“Loop! 11:28:06”
“Loop! 11:28:06”
“Loop! 11:28:06”
“Op te lossen! 11:28:07”
“Op te lossen! 11:28:08”
“Op te lossen! 11:28:09”

De beloften lossen in de volgorde zoals we verwachten, maar de loop zelf is snel, stabiel, en synchroon. Na het bekijken van de MDN polyfill voor het verminderen van (zijn), is dit zinvol. Er is niets asynchrone over een while() loop het activeren van de callback() over en weer, en dat is wat er gebeurt onder de motorkap:

while (k < len) {
als (k, o) {
waarde = terugbellen(waarde, o[k], k, o);
}
k++;
}

Met dat alles in gedachten, de echte magie gebeurt in dit stuk hier:

terug previousPromise.dan(() => {
terug methodThatReturnsAPromise(nextID)
});

Elk moment van onze terugbel branden we weer een belofte die wordt herleid tot een andere belofte. En terwijl het verminderen van (zijn) niet wachten voor een besluit plaats te nemen, het voordeel biedt is de mogelijkheid om iets terug in dezelfde terugbellen na elke run, een uniek kenmerk te verminderen(). Als resultaat, kunnen we bouwen aan een keten van beloften die oplossen in meer beloften, waardoor alles wat mooi en opeenvolgend:

nieuwe Belofte( (lossen, weigeren) => {
// Belofte #1

het oplossen van();
}).dan( (resultaat) => {
// Belofte #2

return result;
}).dan( (resultaat) => {
// Belofte #3

return result;
}); // … en ga zo maar door!

Dit alles moet ook laten zien waarom kunnen we niet gewoon terug een enkele, nieuwe belofte elke iteratie. Omdat de lus loopt synchroon, elke belofte zal worden onmiddellijk in brand gestoken, in plaats van te wachten voor hen gemaakt.

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

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

terug nieuwe Belofte((lossen, weigeren) => {
setTimeout(() => {
console.log(`Oplossen! ${dayjs().de indeling (hh:mm:ss’)}`);
het oplossen van(nextID);
}, 1000);
});
}, Belofte.het oplossen van());

In onze console:

“Loop! 11:31:20”
“Loop! 11:31:20”
“Loop! 11:31:20”
“Op te lossen! 11:31:21”
“Op te lossen! 11:31:21”
“Op te lossen! 11:31:21”

Is het mogelijk om te wachten totdat alle verwerking is voltooid voordat u iets anders aan het doen? Ja. Het synchrone karakter van het verminderen van (zijn) betekent niet dat je niet kunt gooien van een partij na elk item heeft volledig verwerkt. Kijken:

functie methodThatReturnsAPromise(id) {
terug nieuwe Belofte((lossen, weigeren) => {
setTimeout(() => {
console.log(`Processing ${id}`);
het oplossen van(id);
}, 1000);
});
}

laten resultaat = [1,2,3].verminderen( (accumulatorPromise, nextID) => {
terug accumulatorPromise.dan(() => {
terug methodThatReturnsAPromise(nextID);
});
}, Belofte.het oplossen van());

resultaat.dan is het(e => {
console.log(“Resolutie is compleet! Laat de partij.”)
});

Omdat alles wat we terug in onze callback is een geketende belofte, dat is alles wat we krijgen als de lus klaar is: een belofte. Na dat, wij kunnen echter we willen, zelfs lang na te verminderen() heeft haar beloop.

Waarom niet een andere Array methoden werken?

Vergeet niet, onder de motorkap van het verminderen van () we zitten niet te wachten voor onze callback te voltooien voordat we verder gaan naar het volgende item. Het is volledig synchroon lopen. Hetzelfde geldt voor al die andere methoden:

  • Array.prototype.kaart()
  • Array.prototype.forEach()
  • Array.prototype.filter()
  • Array.prototype.sommige()
  • Array.prototype.elke()

Maar verminderen() is bijzonder.

Wij vinden dat de reden verminderen() werkt voor ons is, omdat we in staat om terug te keren iets terug voor onze callback (namelijk, een belofte), die kunnen we dan voortbouwen op de door het oplossen in een andere belofte. Met al deze andere methoden, maar omdat we het gewoon niet kunnen passeren van een argument naar onze callback die is geretourneerd door ons terugbellen. In plaats daarvan, elk van deze callback argumenten van tevoren zijn vastgesteld, waardoor het voor ons onmogelijk te benutten ze voor iets als sequentiële belofte resolutie.

[1,2,3].kaart((item [index array]) => [waarde]);
[1,2,3].filter((item [index array]) => [boolean]);
[1,2,3].sommige((item [index array]) => [boolean]);
[1,2,3].elke((item [index array]) => [boolean]);

Ik hoop dat dit helpt!

Tenminste, ik hoop dat dit helpt enig licht werpen op de reden waarom het verminderen van (de) is bij uitstek geschikt om te verwerken belooft op deze manier, en misschien geven u een beter begrip van hoe vaak Array methoden werken onder de motorkap. Heb ik iets gemist? Iets verkeerd? Laat het me weten!