Node.js Generators & Vertaa takaisinsoittoihin
⚡ Älykäs yhteenveto
GeneratorNode.js:n s-metodit ovat funktioita, joiden suorituksen voi keskeyttää ja jatkaa tarvittaessa. yield-avainsana keskeyttää funktion, next-metodi jatkaa sitä, ja tämä paritus litistää syvälle sisäkkäiset takaisinkutsut luettavaan peräkkäiseen koodiin.

Tässä läpikäynnissä tarkastelemme generaattoreita ja sitä, miten ne eroavat takaisinkutsuista.
Mitä generaattorit ovat?
Generatortuli suosituksi vuonna Node.js sen takia, mitä he pystyvät tekemään.
- Generators ovat toimintojen suorituksia, jotka voidaan keskeyttää ja jatkaa myöhemmin.
- Generators-muuttujat ovat hyödyllisiä esimerkiksi "laiskan suorituksen" kaltaisille käsitteille. Keskeyttämällä suorituksen ja jatkamalla sitä halutessaan arvot noudetaan vain tarvittaessa.
Generators:llä on kaksi keskeistä menetelmää alla.
- Tuottomenetelmä — kutsutaan funktion sisällä pysäyttämään suoritus täsmälleen rivillä, jolla
yieldnäkyviin. - Seuraava menetelmä — kutsutaan pääsovelluksesta palauttaakseen tuottamattoman funktion. Suoritus jatkuu seuraavaan vaiheeseen asti
yieldtai funktion loppuun asti.
Katsotaanpa esimerkkiä generaattoreiden käytöstä. Esimerkissä määritellään yksinkertainen Add-funktio, joka laskee yhteen kaksi lukua ja pysäyttää suorituksen eri kohdissa havainnollistaen generaattoreiden toimintaa.
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 Selitys
- Ensimmäinen vaihe on generaattorifunktion määrittely. Huomaa
*lisättyfunctionavainsana. Funktiota kutsutaanAddja ottaa parametrinx. -
yieldavainsana on generaattoreille ominainen ja keskeyttää funktion suorituksen kesken sen. Suoritus pysähtyy tässä, kunnesnext()vedotaan. - Toinen
yieldsekä keskeyttää funktion että vastaanottaa arvon: mikä tahansa, joka välitetään seuraavallenext()puhelusta tulee arvony. - kutsumus
gen.next()ensimmäisellä kerralla funktio suoritetaan ensimmäiseenyieldSoittaminengen.next(6)jatkaa sitä, antaa 6 tehtäväänyja palauttaax + y.
⚠️ Korjaus: alkuperäisessä listauksessa oli rivi y = 6 heti jälkeen var y = yield(null);Tuo sijoitus korvaa minkä tahansa arvon next() tarvikkeita, mikä tekee esimerkin tarkoituksen tyhjäksi ja tekee toisesta yield merkityksetön. Arvo välitetään nyt sisään gen.next(6), jolla generaattori todellisuudessa vastaanottaa dataa soittajalta.
Takaisinkutsut vs. generaattorit
Generatorratkaisevat niin sanotun callback-helvetin. Callback-funktiot joskus uppoutuvat niin syvälle sisäkkäin Node.js-sovelluksen kehityksen aikana, että niitä on vaikea seurata.
Tässä kohtaa generaattorit auttavat. Yksi yleisimmistä esimerkeistä on ajastinfunktioiden luominen. Alla oleva esimerkki rakentaa yksinkertaisen aikaviivefunktion ja kutsuu sitä sitten 1000, 2000 ja 3000 ms:n viiveillä.
Vaihe 1) Määrittele takaisinkutsufunktio tarvittavalla aikaviivekoodilla.
function Timedelay(ptime, callback) { setTimeout(function() { callback("Pausing for " + ptime); }, ptime); // the delay must use ptime, the parameter name }
Code Selitys
- Funktio nimeltä
Timedelayluodaan parametrillaptime, joka kertoo millisekuntien määrän, jonka aikana toisto keskeytetään. setTimeoutodottaa niin monta millisekuntia ja kutsuu sitten annettua takaisinkutsua.
⚠️ Korjaus: alkuperäinen koodi meni läpi time toisena argumenttina setTimeout, mutta ei nimettyä muuttujaa time on olemassa — parametri on ptimeKuten kirjoitettu, se heittää ReferenceError: time is not defined.
Vaihe 2) Tarkastellaan nyt koodia, jossa käytetään takaisinkutsuja. 1000, 2000 ja 3000 millisekunnin viiveiden ketjuttamiseksi takaisinkutsut sijoitetaan sisäkkäin.
Timedelay(1000, function(message) { console.log(message); Timedelay(2000, function(message) { console.log(message); Timedelay(3000, function(message) { console.log(message); }); }); });
Code Selitys
Timedelaykutsutaan takaisinkutsufunktiona, jonka arvo on 1000.- Tuon takaisinsoiton sisällä
Timedelaykutsutaan uudelleen numerolla 2000. - Sen sisällä,
Timedelaykutsutaan kolmannen kerran numerolla 3000.
⚠️ Korjaus: jokainen alkuperäisen lokitiedoston takaisinsoitto msg, mutta parametri on nimetty messageKaikki kolme riviä heittivät ReferenceError: msg is not defined.
Yllä olevasta koodista näet, että se sotkeutuu jokaisen lisäkutsun myötä – tätä sisäkkäisyyttä callback hell kuvaa.
Vaihe 3) Katsotaanpa samaa toimintaa generaattoreilla kirjoitettuna. Sarjassa näkyy kolme tasaista viiva kolmen sisäkkäisyyden tason sijaan.
function* Messages() { console.log(yield Timedelay(1000)); console.log(yield Timedelay(2000)); console.log(yield Timedelay(3000)); }
Code Selitys
- Generaattorifunktio määritellään kutsumaan
Timedelaytoiminto. yieldkäytetään yhdessäTimedelayja parametrin arvoksi 1000.yieldkäytetään uudelleen luvun 2000 ja vielä kerran luvun 3000 kanssa.
⚠️ Korjaus: alkuperäinen kirjoitti console,log(...) pilkulla pisteen sijaan kaikilla kolmella rivillä, mikä on syntaksivirhe ja estää tiedostoa jäsentämästä lainkaan.
Kuinka ajaa a Generator juoksijafunktiolla
Yllä oleva generaattori lukee kauniisti, mutta ei tee mitään itsekseen. Tämä on yksityiskohta, joka useimmissa selityksissä jätetään huomiotta, ja siksi litteä versio voi näyttää taianomaiselta. Generaattori on pysäytetty kone, ja jonkin on painettava nappia jatkuvasti. Kutsutaan Messages() luo vain generaattoriobjektin ja palauttaa arvon välittömästi; ajastin ei koskaan käynnisty eikä mitään tulostu.
Puuttuva pala on juoksija: pieni funktio, joka kutsuu next(), odottaa asynkronisen työn valmistumista ja kutsuu sitten next() jälleen tuloksen kanssa.
// 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);
Kolme asiaa tekevät tästä toimivan. Ensinnäkin, Timedelay ei enää ota suoraan takaisinkutsua vastaan; se palauttaa funktion, jonka suorittaja voi kutsua, ja luovuttaa ohjauksen takaisin ajastimen käynnistyessä. Toiseksi, it.next(value) syöttää edellisen tuloksen keskeytettyyn generaattoriin, mikä mahdollistaa yield ilmestyä tehtävän oikealle puolelle. Kolmanneksi, juoksija pysähtyy heti, kun result.done tulee tosiksi, joten rekursio päättyy luonnollisesti. Tämä juoksijakuvio on juuri se, mitä kirjastot, kuten co, tarjosivat, ja se on mekanismi, joka async/await myöhemmin imeytyi itse kieleen.
Generators vs. async/await: Kumpaa käyttää
Generatorolivat ennen standarditapa kirjoittaa tasaista asynkronista koodia async/await oli olemassa, ja molemmilla on sama perusajatus funktion keskeyttämisestä kesken suorituksen. Solmu on tukenut async/await natiivisti versiosta 7.6 lähtien, mikä muuttaa suositusta kaikille, jotka aloittavat uuden työn tänään.
| Aspect | Generators | async / salahankkeensa |
|---|---|---|
| Juoksijaa tarvitaan | Kyllä, sinun tai kirjaston kirjoittama | Ei, sisäänrakennettuna kieleen |
| Keskeytä avainsana | yield |
await |
| Virheiden käsittely | Juoksijan on välitettävä virheet eteenpäin | Tavallinen yritys ja kiinniotto |
| Voi keskeyttää kesken kaiken | Kyllä, ja sitä voidaan jatkaa valikoivasti | Ei, kestää loppuun asti |
| Parasta | Laiskat sekvenssit ja loputtomat virrat | Tavallinen asynkroninen työ |
Käyttää async/await asynkroniselle virtaukselle uudessa koodissa, koska se poistaa juoksijan ja palauttaa tavalliset try and catch -menetelmät. Pidä generaattorit vain siinä, mitä ne tekevät hyvin: arvojen tuottaminen laiskasti, pitkien tai äärettömien sekvenssien iteroiminen ja kaikki tapaukset, joissa kuluttaja päättää tarkalleen, milloin seuraava arvo lasketaan.
Yhteinen Generator Virheet ja niiden korjaaminen
Useimmat generaattorin ongelmat johtuvat siitä, että unohdamme funktion olevan inertti, kunnes jokin kutsuu next(), tai pienistä syntaksin virheistä tähden ympärillä. Jokainen alla oleva oire nimeää syynsä ja ratkaisunsa.
- Mitään ei tapahdu, kun generaattoria kutsutaan: Kutsuminen palauttaa iteraattorin, ei tulosta.
next()tai anna se juoksijalle. - Syntaksivirhe tuotossa: tähti puuttuu.
yieldon voimassa vain sisälläfunction*. - next()-funktion arvo saapuu määrittelemättömänä: ensimmäinen
next()Kutsu ei voi toimittaa arvoa, koska generaattori ei ole saavuttanutyieldvielä. Lähetä dataa toisesta puhelusta eteenpäin. - Ääretön silmukka juoksijassa:
result.doneei koskaan tarkisteta, jotennext()kutsutaan jatkuvasti lopun jälkeen. Palauta aina, kun "done" on "true". - Virheet katoavat hiljaa: Asynkronisen vaiheen sisällä olevaa hylkäystä ei koskaan välitetä takaisin generaattorille. Lähetä se eteenpäin
it.throw(err)jotta generaattorin sisällä kirjoitettu try and catch voi todella nähdä ja käsitellä sen.




