Node.js Generators & Usporedi s povratnim pozivima
โก Pametni saลพetak
GeneratorU Node.js-u su funkcije ฤije se izvrลกavanje moลพe suspendirati i nastaviti na zahtjev. Kljuฤna rijeฤ yield pauzira funkciju, metoda next je nastavlja, a to uparivanje izravnava duboko ugnijeลพฤene povratne pozive u ฤitljiv sekvencijalni kod.

U ovom vodiฤu pogledat ฤemo generatore i kako se razlikuju od povratnih poziva.
ล to su generatori?
Generatorpostao je popularan u Node.js zbog onoga ลกto su sposobni uฤiniti.
- Generators su izvrลกavanja funkcija koja se mogu obustaviti i nastaviti kasnije.
- Generatorsu korisni za koncepte poput โlijenog izvrลกavanjaโ. Obustavljanjem izvrลกavanja i nastavkom po volji, vrijednosti se povlaฤe samo kada su potrebne.
Generatorimaju dvije kljuฤne metode u nastavku.
- Metoda prinosa โ poziva se unutar funkcije za zaustavljanje izvrลกavanja na toฤno odreฤenom retku
yieldpojavljuje. - Sljedeฤa metoda โ poziva se iz glavne aplikacije za nastavak funkcije koja je dala prinos. Izvrลกavanje se nastavlja do sljedeฤeg
yield, ili do kraja funkcije.
Pogledajmo primjer kako se generatori mogu koristiti. Primjer definira jednostavnu funkciju Zbrajanje koja zbraja dva broja, zaustavljajuฤi izvrลกavanje u razliฤitim toฤkama kako bi se pokazalo kako se generatori ponaลกaju.
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 Objaลกnjenje
- Prvi korak je definirati funkciju generatora. Imajte na umu
*dodano ufunctionkljuฤna rijeฤ. Funkcija se pozivaAddi uzima parametarx. - The
yieldKljuฤna rijeฤ je specifiฤna za generatore i pauzira funkciju usred izvrลกavanja. Izvrลกavanje se ovdje zaustavlja doknext()se poziva. - Drugi
yieldpauzira funkciju i prima vrijednost: ลกto god se prosljeฤuje sljedeฤemnext()poziv postaje vrijednosty. - zvanje
gen.next()prvi put izvrลกava funkciju do prvogyieldPozivanjegen.next(6)nastavlja ga, dodjeljuje 6 doyi vraฤa sex + y.
โ ๏ธ Ispravak: izvorni popis sadrลพavao je redak y = 6 odmah nakon var y = yield(null);Ta dodjela prepisuje bilo koju vrijednost next() zalihe, ลกto poniลกtava svrhu primjera i ฤini drugi yield besmisleno. Vrijednost se sada prenosi kroz gen.next(6), ลกto je naฤin na koji generator zapravo prima podatke od pozivatelja.
Povratni pozivi u odnosu na generatore
Generatorrjeลกavaju problem poznat kao pakao povratnih poziva. Funkcije povratnih poziva ponekad postanu toliko duboko ugnijeลพฤene tijekom razvoja Node.js aplikacije da ih je teลกko pratiti.
Tu generatori pomaลพu. Jedan od najฤeลกฤih primjera je stvaranje funkcija timera. Primjer u nastavku gradi jednostavnu funkciju vremenskog kaลกnjenja, a zatim je poziva s kaลกnjenjima od 1000, 2000 i 3000 ms.
Korak 1) Definirajte funkciju povratnog poziva s potrebnim kodom vremenskog kaลกnjenja.
function Timedelay(ptime, callback) { setTimeout(function() { callback("Pausing for " + ptime); }, ptime); // the delay must use ptime, the parameter name }
Code Objaลกnjenje
- Funkcija pod nazivom
Timedelaystvara se s parametromptime, koji sadrลพi broj milisekundi za pauzu. setTimeoutฤeka taj broj milisekundi, a zatim poziva navedeni povratni poziv.
โ ๏ธ Ispravak: izvorni kod je proลกao time kao drugi argument za setTimeoutali nema imenovane varijable time postoji โ parametar je ptimeKao ลกto je napisano, baca ReferenceError: time is not defined.
Korak 2) Sada razmotrite kod koji koristi povratne pozive. Za lanฤano povezivanje kaลกnjenja od 1000, 2000 i 3000 milisekundi, povratni pozivi se ugnijezde jedan u drugi.
Timedelay(1000, function(message) { console.log(message); Timedelay(2000, function(message) { console.log(message); Timedelay(3000, function(message) { console.log(message); }); }); });
Code Objaลกnjenje
Timedelaypoziva se kao povratni poziv s vrijednoลกฤu 1000.- Unutar tog povratnog poziva,
Timedelayponovno se poziva s 2000. - Unutar tog,
Timedelayse treฤi put zove s 3000.
โ ๏ธ Ispravak: svaki povratni poziv u izvorniku zabiljeลพen msg, ali parametar je imenovan messageSve tri linije su bacile ReferenceError: msg is not defined.
Iz gornjeg koda moลพete vidjeti da postaje sve neuredniji sa svakim dodatnim pozivom - ovo ugnijeลพฤivanje je ono ลกto callback hell opisuje.
Korak 3) Sada pogledajte isto ponaลกanje zapisano generatorima. Niz se ฤita kao tri ravne linije, a ne kao tri razine ugnijeลพฤivanja.
function* Messages() { console.log(yield Timedelay(1000)); console.log(yield Timedelay(2000)); console.log(yield Timedelay(3000)); }
Code Objaลกnjenje
- Generatorska funkcija je definirana za pozivanje
Timedelayfunkcija. yieldkoristi se saTimedelayi 1000 kao vrijednost parametra.yieldponovno se koristi s 2000, i joลก jednom s 3000.
โ ๏ธ Ispravak: original je napisao console,log(...) sa zarezom umjesto toฤke u sva tri retka, ลกto je sintaktiฤka pogreลกka i uopฤe sprjeฤava parsiranje datoteke.
Kako pokrenuti a Generator s funkcijom trkaฤa
Gornji generator se lijepo ฤita, ali sam od sebe neฤe niลกta uฤiniti. To je detalj koji veฤina objaลกnjenja izostavlja i to je razlog zaลกto ravna verzija moลพe izgledati kao magija. Generator je pauzirani stroj i neลกto mora stalno pritiskati gumb. Pozivanje Messages() samo stvara objekt generatora i odmah se vraฤa; timer se nikada ne pokreฤe i niลกta se ne ispisuje.
Nedostajuฤi dio je trkaฤ: mala funkcija koja poziva next(), ฤeka da se asinhroni rad zavrลกi, a zatim poziva next() opet s rezultatom.
// 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);
Tri su toฤke koje ovo ฤine uspjeลกnim. Prvo, Timedelay viลกe ne prima izravno povratni poziv; vraฤa funkciju koju izvrลกavatelj moลพe pozvati, vraฤajuฤi kontrolu kada se timer aktivira. Drugo, it.next(value) unosi prethodni rezultat u pauzirani generator, ลกto omoguฤuje yield da se pojavi s desne strane zadatka. Treฤe, trkaฤ se zaustavlja ฤim result.done postaje istinito, pa se rekurzija prirodno zavrลกava. Ovaj obrazac izvrลกavanja je upravo ono ลกto su biblioteke poput co pruลพile, i to je mehanizam koji async/await kasnije apsorbiran u sam jezik.
Generators vs async/await: Koji koristiti
Generatorbili su standardni naฤin pisanja ravnog asinhronog koda prije async/await postojao je, i oboje dijele istu temeljnu ideju pauziranja funkcije usred izvrลกavanja. Node je podrลพao async/await izvorno od verzije 7.6, ลกto mijenja preporuku za sve koji danas zapoฤinju novi posao.
| Aspekt | Generators | asinkroni/ฤekaj |
|---|---|---|
| Potreban trkaฤ | Da, napisali ste vi ili knjiลพnica | Ne, ugraฤeno u jezik |
| Pauziraj kljuฤnu rijeฤ | yield |
await |
| Rukovanje pogreลกkama | Trkaฤ mora proslijediti greลกke | Obiฤan pokuลกaj i ulov |
| Moลพe se pauzirati na pola puta | Da, i moลพe se selektivno nastaviti | Ne, traje do zavrลกetka |
| Najbolje za | Lijene sekvence i beskonaฤni streamovi | Obiฤan asinhroni rad |
Koristiti async/await za asinkroni tok u novom kodu, buduฤi da uklanja runner i vraฤa obiฤne try and catch funkcije. Zadrลพite generatore za ono ลกto samo oni dobro rade: lijeno generiranje vrijednosti, iteriranje velikih ili beskonaฤnih nizova i svaki sluฤaj gdje potroลกaฤ toฤno odluฤuje kada se izraฤunava sljedeฤa vrijednost.
Zajedniฤki Generator Greลกke i kako ih ispraviti
Veฤina problema s generatorom nastaje zbog zaboravljanja da je funkcija inertna dok neลกto ne pozove next(), ili zbog malih sintaktiฤkih omaลกki oko zvjezdice. Svaki simptom u nastavku navodi svoj uzrok i rjeลกenje.
- Niลกta se ne dogaฤa kada se pozove generator: Pozivanje vraฤa iterator, a ne rezultat. Poziv
next()ili ga dodajte trkaฤu. - Sintaktiฤka pogreลกka pri prinosu: zvjezdica nedostaje.
yieldvrijedi samo unutarfunction*. - Vrijednost iz next() stiลพe nedefinirana: prvi
next()poziv ne moลพe isporuฤiti vrijednost jer generator nije dosegaoyieldjoลก. Poลกaljite podatke od drugog poziva nadalje. - Beskonaฤna petlja u trkaฤu:
result.donenikad se ne provjerava, panext()se stalno poziva i nakon kraja. Uvijek vrati kada je gotovo je istina. - Greลกke tiho nestaju: Odbacivanje unutar asinhronog koraka se nikada ne vraฤa u generator. Proslijedite ga s
it.throw(err)tako da naredbe try and catch napisane unutar generatora zapravo mogu to vidjeti i obraditi.




