Node.js Generators & Võrdle tagasihelistamisega

⚡ Nutikas kokkuvõte

GeneratorNode.js-i s-d on funktsioonid, mille täitmise saab nõudmisel peatada ja jätkata. Märksõna yield peatab funktsiooni, järgmine meetod jätkab seda ja see sidumine tasandab sügavalt pesastatud tagasihelistamisfunktsioonid loetavaks järjestikuseks koodiks.

  • ⏸️ Põhikäitumine: Generaator peatub iga saagikuse korral ja jätkab täpselt sealt, kus see peatus, kui järgmine funktsioon välja kutsutakse.
  • ✳️ Deklaratsioon: Tärn märksõna „function” järel tähistab funktsiooni generaatorina, mitte tavalise funktsioonina.
  • 🔄 Kahesuunaline kanal: Funktsioonile next edastatud väärtus saab funktsiooni peatanud saagikuse avaldise tulemuseks.
  • 🪆 Tagasihelistamise põrgu: Pesastatud tagasihelistused muutuvad sammude lisamisega loetamatuks ja just selle probleemi lahendamiseks võetigi kasutusele generaatorid.
  • 🏃 Nõutav jooksja: Generaator ei käivita iseennast; asünkroonse töö jätkamiseks peab jooksjafunktsioon korduvalt kutsuma funktsiooni next.
  • 🦥 Laisk täitmine: Peatamine ja jätkamine võimaldab programmil väärtusi hankida ainult siis, kui neid vajatakse.

Node.js GeneratorVõrreldes tagasihelistustega

Selles ülevaates vaatleme generaatoreid ja seda, kuidas need erinevad tagasihelistustest.

Mis on generaatorid?

Generators sai populaarseks Node.js selle pärast, milleks nad võimelised on.

  • Generators on funktsioonide täitmised, mida saab peatada ja hiljem jätkata.
  • Generators on kasulikud selliste kontseptsioonide puhul nagu „laisk täitmine“. Täitmise peatamise ja suvalise jätkamise abil hangitakse väärtused ainult siis, kui neid vaja on.

Generatorneil on kaks peamist meetodit allpool.

  1. Saagise meetod — kutsutakse funktsiooni sees välja, et peatada täitmine täpselt sellel real, kus yield ilmub.
  2. Järgmine meetod — kutsutakse välja põhirakendusest, et jätkata funktsiooni, mis on andnud tulemuseks tulemuse. Täitmine jätkub kuni järgmise käivituseni yieldvõi kuni funktsiooni lõpuni.

Vaatame näidet generaatorite kasutamise kohta. Näide defineerib lihtsa liitmisfunktsiooni, mis liidab kaks arvu, peatades täitmise erinevates punktides, et näidata generaatorite käitumist.

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 Selgitus

  1. Esimene samm on generaatori funktsiooni defineerimine. Pange tähele * lisatud function märksõna. Funktsiooni nimetatakse Add ja võtab parameetri x.
  2. . yield märksõna on generaatoritele omane ja peatab funktsiooni täitmise keskel. Täitmine peatub siin kuni next() kutsutakse esile.
  3. 2. yield nii peatab funktsiooni kui ka saab väärtuse: mis iganes järgmisele edastatakse next() kõnest saab väärtus y.
  4. Üleskutse gen.next() esimesel korral käivitatakse funktsioon esimese yieldHelistades gen.next(6) jätkab seda, määrab 6-le yja tagastab x + y.

⚠️ Parandus: algne nimekiri sisaldas rida y = 6 kohe pärast var y = yield(null);See omistamine kirjutab üle mis tahes väärtuse. next() tarvikud, mis muudab näite eesmärgi kehtetuks ja muudab teise yield mõttetu. Väärtus antakse nüüd läbi gen.next(6), mis ongi see, kuidas generaator helistajalt andmeid saab.

Tagasihelistamised vs generaatorid

Generatorlahendavad probleemi, mida tuntakse tagasihelistuspõrguna. Tagasihelistusfunktsioonid jäävad Node.js rakenduse arendamise käigus mõnikord nii sügavale pesasse, et neid on raske jälgida.

Siin tulevad appi generaatorid. Üks levinumaid näiteid on taimerifunktsioonide loomine. Allolev näide loob lihtsa ajanihkefunktsiooni ja kutsub selle seejärel välja viivitustega 1000, 2000 ja 3000 ms.

Step 1) Defineeri tagasihelistusfunktsioon vajaliku ajanihke koodiga.

Tagasihelistamine vs. Generators

function Timedelay(ptime, callback) {

    setTimeout(function() {

        callback("Pausing for " + ptime);

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

Code Selgitus

  1. Funktsioon nimega Timedelay luuakse parameetriga ptime, mis näitab millisekundite arvu, milleks paus teha.
  2. setTimeout ootab nii mitu millisekundit ja seejärel kutsub esile antud tagasihelistamise.

⚠️ Parandus: algne kood läbis time teise argumendina setTimeout, aga nimetamata muutujat time eksisteerib — parameeter on ptimeNagu kirjutatud, viskab see ReferenceError: time is not defined.

Step 2) Nüüd vaatleme koodi, mis kasutab tagasihelistusi. 1000, 2000 ja 3000 millisekundi pikkuste viivituste aheldamiseks paigutatakse tagasihelistused üksteise sisse.

Tagasihelistamine vs. Generators

Timedelay(1000, function(message) {

    console.log(message);

    Timedelay(2000, function(message) {

        console.log(message);

        Timedelay(3000, function(message) {

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

Code Selgitus

  1. Timedelay kutsutakse tagasihelistusfunktsioonina, mille väärtuseks on 1000.
  2. Selle tagasihelistamise sees Timedelay nimetatakse uuesti numbriga 2000.
  3. Selle sees Timedelay kutsutakse kolmandat korda numbriga 3000.

⚠️ Parandus: iga tagasihelistamine algses logis msg, aga parameetri nimi on messageKõik kolm rida viskasid ReferenceError: msg is not defined.

Ülaltoodud koodist näete, et iga täiendava kõnega muutub see segasemaks – see pesastamine ongi see, mida tagasihelistamise põrgu kirjeldab.

Step 3) Nüüd vaadake sama käitumist generaatoritega kirjutatuna. Jada loetakse kolme tasase joonena, mitte kolme pesastamise tasemena.

Tagasihelistamine vs. Generators

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

Code Selgitus

  1. Generaatori funktsioon on defineeritud kutsuma Timedelay funktsiooni.
  2. yield kasutatakse koos Timedelay ja parameetri väärtuseks 1000.
  3. yield kasutatakse uuesti koos arvuga 2000 ja veel kord koos arvuga 3000.

⚠️ Parandus: originaal kirjutas console,log(...) komaga punkti asemel kõigil kolmel real, mis on süntaksiviga ja takistab faili täielikku parsimist.

Kuidas joosta a Generator jooksja funktsiooniga

Ülaltoodud generaator loeb ilusti, aga ei tee ise midagi. See on detail, mida enamik selgitusi ei maini, ja see on põhjus, miks tasane versioon võib tunduda maagiline. Generaator on pausil masin ja midagi peab nuppu pidevalt all hoidma. Helistamine Messages() loob lihtsalt generaatori objekti ja tagastab kohe tulemuse; taimerit ei käivitata ja midagi ei prindita.

Puuduv tükk on jooksja: väike funktsioon, mis kutsub esile next(), ootab asünkroonse töö lõppu ja seejärel kutsub next() jälle tulemusega.

// 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);

Selle toimimiseks on vaja kolme punkti. Esiteks, Timedelay ei võta enam otse tagasihelistust vastu; see tagastab funktsiooni, mida käivitaja saab käivitada, andes juhtimise tagasi, kui taimer käivitub. Teiseks, it.next(value) edastab eelmise tulemuse peatatud generaatorisse, mis võimaldab yield ülesande paremal küljel ilmuma. Kolmandaks peatub jooksja niipea, kui result.done saab tõeks, seega rekursioon lõpeb loomulikult. See jooksja muster on täpselt see, mida teegid, näiteks co, pakkusid, ja see on mehhanism, mis async/await hiljem imendus see keelde endasse.

Generators vs async/await: Kumba kasutada

Generatorolid standardne viis lameda asünkroonse koodi kirjutamiseks enne async/await eksisteeris ja neil kahel on sama põhiidee funktsiooni poolelijätmiseks. Sõlm on toetanud async/await natiivselt alates versioonist 7.6, mis muudab soovitust kõigile, kes täna uut tööd alustavad.

Aspekt Generators asünkrooni/oota
Jooksjat vaja Jah, teie või raamatukogu kirjutatud Ei, see on keelde sisse ehitatud
Märksõna peatamine yield await
Viga käsitlemisel Jooksja peab vead edastama Tavaline katse ja püüdmine
Võib pooleli pausi teha Jah, ja seda saab valikuliselt jätkata Ei, jookseb lõpuni
Parim on Laisad järjestused ja lõpmatud vooged Tavaline asünkroonne töö

Kasutama async/await uue koodi asünkroonse voo jaoks, kuna see eemaldab jooksja ja taastab tavalise try and catch'i. Hoidke generaatorid alles ainult selleks, mida nad hästi teevad: väärtuste laisk genereerimine, pikkade või lõpmatute järjestuste itereerimine ja iga juhtum, kus tarbija otsustab täpselt, millal järgmine väärtus arvutatakse.

ühine Generator Vead ja kuidas neid parandada

Enamik generaatori probleeme tekib funktsiooni inertsuse unustamisest kuni miski kutsub esile funktsiooni next()või väikestest süntaksilibistustest tärni ümber. Iga allpool olev sümptom nimetab selle põhjuse ja lahenduse.

  • Generaatori kutsumisel ei juhtu midagi: Selle kutsumine tagastab iteraatori, mitte tulemuse. next()või anna see jooksjale edasi.
  • Süntaksiviga saagikuse kohta: tärn puudub. yield kehtib ainult seespool function*.
  • Väärtus funktsioonist next() saabub määratlemata kujul: 1. next() kutse ei saa väärtust edastada, kuna generaator pole veel saavutanud yield veel. Saatke andmeid alates teisest kõnest.
  • Lõpmatu tsükkel jooksjas: result.done seda ei kontrollita kunagi, seega next() kutsutakse välja ka pärast lõppu. Tagasta alati, kui „done” on tõene.
  • Vead kaovad vaikselt: Asünkroonse sammu sees olevat tagasilükkamist ei edastata kunagi generaatorile tagasi. Edastage see it.throw(err) nii et generaatori sees kirjutatud try and catch seda tegelikult näeb ja käsitleb.

KKK

Objekt kahe omadusega: value, hoides kinni kõigest, mis loovutati, ja done, tõeväärtus, mis muutub tõeseks, kui generaator lõpetab töö. Jooksjate test tehakse, et teada saada, millal peatuda.

Jah. Lõputu tsükliga generaator on turvaline, sest enne next() kutsumist ei toimu arvutusi. See teeb generaatorid hästi sobivaks identifikaatorite voogude või lehekülgedega nummerdatud tulemuste jaoks.

Jah. AI tööriistad vahetavad saagikuse ootamise vastu ja loobuvad jooksjast täielikult. RevVaadake laisaid järjestusi hoolikalt, sest generaatoril, mis kunagi ei lõpeta tööd, puudub otsene asünkroonse ootamise ekvivalent.

AI assistendid trace kas jooksja lõpetas järgmise kutsumise, kas jätkamiskutset ei kutsutud kunagi välja või kas valmisolekuni ei jõutud kunagi. Need kolm tegurit põhjustavad peaaegu iga seiskunud generaatori.

Ei. Asünkroongeneraator on deklareeritud. async function* ja on tarbitud koos for await...ofSee ühendab mõlemad ideed ja sobib asünkroonsete andmete, näiteks andmebaasikursorite, voogedastamiseks.

Võta see postitus kokku järgmiselt: