node.js Generators & Sammenlign med tilbagekald
โก Smart opsummering
Generator`s` i Node.js er funktioner, hvis udfรธrelse kan suspenderes og genoptages efter behov. `yield`-nรธgleordet sรฆtter funktionen pรฅ pause, `next`-metoden genoptager den, og denne parring flader dybt indlejrede callbacks ud til lรฆsbar sekventiel kode.

I denne gennemgang ser vi pรฅ generatorer og hvordan de adskiller sig fra callbacks.
Hvad er generatorer?
Generatorblev populรฆr i node.js pรฅ grund af hvad de er i stand til at gรธre.
- Generators er funktionsudfรธrelser, der kan suspenderes og genoptages pรฅ et senere tidspunkt.
- Generators er nyttige til koncepter som "doven udfรธrelse". Ved at suspendere udfรธrelsen og genoptage den efter behov, hentes vรฆrdier kun, nรฅr der er behov for dem.
Generators har de to vigtigste metoder nedenfor.
- Udbyttemetode โ kaldes inde i en funktion for at stoppe udfรธrelsen pรฅ den prรฆcise linje, hvor
yieldkommer til syne. - Nรฆste metode โ kaldet fra hovedapplikationen for at genoptage en funktion, der har givet afkald. Udfรธrelsen fortsรฆtter indtil den nรฆste
yield, eller indtil funktionen er slut.
Lad os se pรฅ et eksempel pรฅ, hvordan generatorer kan bruges. Eksemplet definerer en simpel Add-funktion, der lรฆgger to tal sammen og stopper udfรธrelsen pรฅ forskellige punkter for at vise, hvordan generatorer opfรธrer sig.
function* Add(x) { yield x + 1; // The value passed into the second next() call lands here var y = yield(null); return x + y; } var gen = Add(5); gen.next(); // runs to the first yield, returns { value: 6, done: false } gen.next(6); // resumes, y becomes 6, returns { value: 11, done: true }
Code Forklaring
- Det fรธrste trin er at definere generatorfunktionen. Bemรฆrk
*tilfรธjet tilfunctionnรธgleord. Funktionen kaldesAddog tager en parameterx. -
yieldNรธgleordet er specifikt for generatorer og sรฆtter funktionen pรฅ pause midt i udfรธrelsen. Udfรธrelsen stopper her indtilnext()pรฅkaldes. - Den anden
yieldbรฅde sรฆtter funktionen pรฅ pause og modtager en vรฆrdi: hvad der end sendes til fรธlgendenext()opkaldet bliver vรฆrdien afy. - Opkald
gen.next()fรธrste gang kรธrer funktionen op til den fรธrsteyieldOpkaldgen.next(6)genoptager det, tildeler 6 tily, og returnererx + y.
โ ๏ธ Rettelse: Den oprindelige liste indeholdt linjen y = 6 umiddelbart efter var y = yield(null);Den tildeling overskriver enhver vรฆrdi next() forsyninger, hvilket modvirker formรฅlet med eksemplet og gรธr det andet yield meningslรธs. Vรฆrdien gives nu videre gennem gen.next(6), hvilket er sรฅdan en generator rent faktisk modtager data fra den, der ringer op.
Tilbagekald vs. generatorer
Generators lรธser problemet kendt som callback-helvede. Callback-funktioner bliver nogle gange sรฅ dybt indlejrede under udviklingen af โโen Node.js-applikation, at de er vanskelige at fรธlge.
Det er her, generatorer hjรฆlper. Et af de mest almindelige eksempler er oprettelse af timerfunktioner. Eksemplet nedenfor bygger en simpel tidsforsinkelsesfunktion og kalder den derefter med forsinkelser pรฅ 1000, 2000 og 3000 ms.
Trin 1) Definer callback-funktionen med den nรธdvendige tidsforsinkelseskode.
function Timedelay(ptime, callback) { setTimeout(function() { callback("Pausing for " + ptime); }, ptime); // the delay must use ptime, the parameter name }
Code Forklaring
- En funktion kaldet
Timedelayer oprettet med en parameterptime, som angiver antallet af millisekunder, der skal holdes pause i. setTimeoutventer i det antal millisekunder og kalder derefter det angivne callback.
โ ๏ธ Rettelse: den oprindelige kode, der blev bestรฅet time som det andet argument til setTimeout, men ingen variabel navngivet time findes โ parameteren er ptimeSom skrevet kaster det ReferenceError: time is not defined.
Trin 2) Overvej nu koden ved hjรฆlp af callbacks. For at kรฆde forsinkelser pรฅ 1000, 2000 og 3000 millisekunder sammen, skal callbacks indlejres inden i hinanden.
Timedelay(1000, function(message) { console.log(message); Timedelay(2000, function(message) { console.log(message); Timedelay(3000, function(message) { console.log(message); }); }); });
Code Forklaring
Timedelaykaldes som et callback med vรฆrdien 1000.- Inde i det tilbagekald,
Timedelaykaldes igen med 2000. - Inde i den ene,
Timedelaykaldes en tredje gang med 3000.
โ ๏ธ Rettelse: hvert tilbagekald i den oprindelige logfรธring msg, men parameteren er navngivet messageAlle tre linjer kastede ReferenceError: msg is not defined.
Fra koden ovenfor kan du se, at det bliver mere rodet med hvert yderligere kald โ denne indlejring er, hvad callback-helvede beskriver.
Trin 3) Se nu den samme opfรธrsel skrevet med generatorer. Sekvensen lรฆses som tre flade linjer i stedet for tre niveauer af indlejring.
function* Messages() { console.log(yield Timedelay(1000)); console.log(yield Timedelay(2000)); console.log(yield Timedelay(3000)); }
Code Forklaring
- En generatorfunktion er defineret til at kalde
Timedelayfunktion. yieldbruges medTimedelayog 1000 som parametervรฆrdi.yieldbruges igen med 2000, og endnu en gang med 3000.
โ ๏ธ Rettelse: originalen skrev console,log(...) med et komma i stedet for et punktum pรฅ alle tre linjer, hvilket er en syntaksfejl og forhindrer filen i at blive parset overhovedet.
Sรฅdan kรธrer du en Generator med en lรธberfunktion
Generatoren ovenfor er smukt lรฆsbar, men kan ikke gรธre noget i sig selv. Dette er den detalje, de fleste forklaringer udelader, og det er grunden til, at den flade version kan ligne magi. En generator er en maskine, der er sat pรฅ pause, og noget skal blive ved med at trykke pรฅ knappen. Opkald Messages() opretter blot generatorobjektet og vender tilbage med det samme; ingen timer starter nogensinde, og intet udskrives.
Den manglende brik er en lรธber: en lille funktion, der kalder next(), venter pรฅ, at det asynkrone arbejde er fรฆrdigt, og kalder derefter next() igen med resultatet.
// Timedelay now hands its result back to the runner function Timedelay(ptime) { return function(resume) { setTimeout(function() { resume("Pausing for " + ptime); }, ptime); }; } // The runner drives the generator to completion function run(generatorFunction) { var it = generatorFunction(); function step(value) { var result = it.next(value); if (result.done) return; // result.value is the function returned by Timedelay result.value(step); } step(); } run(Messages);
Tre punkter fรฅr dette til at fungere. For det fรธrste, Timedelay tager ikke lรฆngere et callback direkte; den returnerer en funktion, som lรธberen kan kalde, og giver kontrollen tilbage, nรฅr timeren aktiveres. For det andet, it.next(value) fรธder det forrige resultat ind i den pausede generator, hvilket er det, der tillader yield at vises pรฅ hรธjre side af en opgave. For det tredje stopper lรธberen, sรฅ snart result.done bliver sand, sรฅ rekursionen afsluttes naturligt. Dette lรธbermรธnster er prรฆcis, hvad biblioteker som co leverede, og det er den mekanisme, der async/await senere absorberet i selve sproget.
Generators vs async/await: Hvilken skal bruges
Generators var standardmรฅden til at skrive flad asynkron kode fรธr async/await eksisterede, og de to deler den samme underliggende idรฉ om at sรฆtte en funktion pรฅ pause midtvejs. Node har understรธttet async/await indbygget siden version 7.6, hvilket รฆndrer anbefalingen for alle, der starter nyt arbejde i dag.
| Aspect | Generators | asynkron/afvent |
|---|---|---|
| Lรธber sรธges | Ja, skrevet af dig eller et bibliotek | Nej, indbygget i sproget |
| Sรฆt sรธgeord pรฅ pause | yield |
await |
| Fejl ved hรฅndtering | Lรธber skal videresende fejl | Almindelig forsรธg og fangst |
| Kan holde pause midtvejs | Ja, og kan genoptages selektivt | Nej, kรธrer til fรฆrdiggรธrelse |
| Bedste for | Dovne sekvenser og uendelige strรธmme | Almindeligt asynkront arbejde |
Brug async/await for asynkront flow i ny kode, da det fjerner runneren og gendanner almindelig try and catch. Behold generatorer til det, de kun gรธr godt: dovent producere vรฆrdier, iterere store eller uendelige sekvenser og ethvert tilfรฆlde, hvor forbrugeren bestemmer prรฆcis, hvornรฅr den nรฆste vรฆrdi beregnes.
Fรฆlles Generator Fejl og hvordan man retter dem
De fleste generatorproblemer skyldes, at man glemmer, at funktionen er inaktiv, indtil noget kalder next(), eller fra smรฅ syntakser, der glider rundt om asterisken. Hvert symptom nedenfor angiver dets รฅrsag og dets lรธsning.
- Der sker ikke noget, nรฅr generatoren kaldes: Et kald af den returnerer en iterator, ikke et resultat.
next(), eller giv den videre til en lรธber. - Syntaksfejl ved udbytte: asterisken mangler.
yielder kun gyldig indenfor enfunction*. - Vรฆrdien fra next() ankommer udefineret: den fรธrste
next()opkaldet kan ikke levere en vรฆrdi, fordi generatoren ikke har nรฅet enyieldendnu. Send data fra det andet opkald og fremefter. - Uendelig lรธkke i en runner:
result.donebliver aldrig tjekket, sรฅnext()bliver ved med at blive kaldt efter slutningen. Returner altid nรฅr fรฆrdig er sandt. - Fejl forsvinder lydlรธst: En afvisning i det asynkrone trin sendes aldrig tilbage til generatoren. Send den videre med
it.throw(err)sรฅ en "try and catch" skrevet inde i generatoren rent faktisk kan se og hรฅndtere den.




