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.

  • ⏸️ Ydinkäyttäytyminen: Generaattori pysähtyy jokaisen tuoton kohdalla ja jatkaa täsmälleen siitä, mihin se pysähtyi, kun seuraavaa kutsutaan.
  • ✳️ ilmoitus: Avainsanan function perässä oleva tähti merkitsee funktion generaattoriksi tavallisen generaattorin sijaan.
  • 🔄 Kaksisuuntainen kanava: next-funktiolle syötetty arvo muuttuu funktion keskeyttäneen yield-lausekkeen tulokseksi.
  • 🪆 Takaisinsoittohelvetti: Sisäkkäiset takaisinkutsutoiminnot muuttuvat lukukelvottomiksi vaiheiden lisäämisen myötä, ja juuri tämän ongelman ratkaisemiseksi generaattorit otettiin käyttöön.
  • 🏃 Vaadittu juoksija: Generaattori ei aja itseään; juoksijafunktion on kutsuttava next-funktiota toistuvasti, jotta asynkroninen työ jatkuisi.
  • 🦥 Laiska toteutus: Keskeyttäminen ja jatkaminen antaa ohjelman noutaa arvoja vain silloin, kun niitä tarvitaan.

Node.js GeneratorVerrattuna takaisinsoittoihin

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.

  1. Tuottomenetelmä — kutsutaan funktion sisällä pysäyttämään suoritus täsmälleen rivillä, jolla yield näkyviin.
  2. 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.

Node.js Generators

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

  1. Ensimmäinen vaihe on generaattorifunktion määrittely. Huomaa * lisätty function avainsana. Funktiota kutsutaan Add ja ottaa parametrin x.
  2. yield avainsana on generaattoreille ominainen ja keskeyttää funktion suorituksen kesken sen. Suoritus pysähtyy tässä, kunnes next() vedotaan.
  3. Toinen yield sekä keskeyttää funktion että vastaanottaa arvon: mikä tahansa, joka välitetään seuraavalle next() puhelusta tulee arvon y.
  4. kutsumus gen.next() ensimmäisellä kerralla funktio suoritetaan ensimmäiseen yieldSoittaminen gen.next(6) jatkaa sitä, antaa 6 tehtävään yja palauttaa x + 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.

Takaisinsoitto vs. Generators

function Timedelay(ptime, callback) {

    setTimeout(function() {

        callback("Pausing for " + ptime);

    }, ptime);   // the delay must use ptime, the parameter name
}

Code Selitys

  1. Funktio nimeltä Timedelay luodaan parametrilla ptime, joka kertoo millisekuntien määrän, jonka aikana toisto keskeytetään.
  2. setTimeout odottaa 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.

Takaisinsoitto vs. Generators

Timedelay(1000, function(message) {

    console.log(message);

    Timedelay(2000, function(message) {

        console.log(message);

        Timedelay(3000, function(message) {

            console.log(message);
        });
    });
});

Code Selitys

  1. Timedelay kutsutaan takaisinkutsufunktiona, jonka arvo on 1000.
  2. Tuon takaisinsoiton sisällä Timedelay kutsutaan uudelleen numerolla 2000.
  3. Sen sisällä, Timedelay kutsutaan 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.

Takaisinsoitto vs. Generators

function* Messages() {
    console.log(yield Timedelay(1000));
    console.log(yield Timedelay(2000));
    console.log(yield Timedelay(3000));
}

Code Selitys

  1. Generaattorifunktio määritellään kutsumaan Timedelay toiminto.
  2. yield käytetään yhdessä Timedelay ja parametrin arvoksi 1000.
  3. yield kä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. yield on 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 saavuttanut yield vielä. Lähetä dataa toisesta puhelusta eteenpäin.
  • Ääretön silmukka juoksijassa: result.done ei koskaan tarkisteta, joten next() 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.

UKK

Objekti, jolla on kaksi ominaisuutta: value, pitäen kiinni kaikesta, mikä luovutettiin, ja done, totuusarvo, joka muuttuu tosiksi, kun generaattori on valmis. Suorittimien testit tehdään sen selvittämiseksi, milloin on aika lopettaa.

Kyllä. Loputtoman silmukan sisältävä generaattori on turvallinen, koska mitään ei tehdä ennen kuin next()-funktiota kutsutaan. Tämän ansiosta generaattorit sopivat hyvin tunnisteiden tai sivutettujen tulosten virroille.

Kyllä. AI työkalut vaihtavat sadon odotukseen ja hylkäävät juoksijan kokonaan. RevTarkastele laiskoja sekvenssejä huolellisesti, koska generaattorilla, joka ei koskaan pääty loppuun, ei ole suoraa async-odotustoimintoa.

AI-avustajat trace lopettiko juoksija seuraavan kutsun, eikö jatkokutsun pyyntöä koskaan kutsuttu vai eikö valmis-tilaan päästy koskaan. Nämä kolme selittävät lähes jokaisen pysähtyneen generaattorin.

Ei. Asynkroninen generaattori on ilmoitettu. async function* ja kulutetaan for await...ofSe yhdistää molemmat ideat ja sopii asynkronisen datan, kuten tietokantakursorien, suoratoistoon.

Tiivistä tämä viesti seuraavasti: