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.

  • โธ๏ธ Osnovno ponaลกanje: Generator se obustavlja na svakom yield-u i nastavlja toฤno tamo gdje je stao kada se pozove next.
  • โœณ๏ธ Izjava: Zvjezdica iza kljuฤne rijeฤi function oznaฤava funkciju kao generator, a ne kao obiฤnu.
  • ๐Ÿ”„ Dvosmjerni kanal: Vrijednost proslijeฤ‘ena u next postaje rezultat izraza yield koji je pauzirao funkciju.
  • ๐Ÿช† Pakao povratnih poziva: Ugnijeลพฤ‘eni povratni pozivi postaju neฤitljivi kako se dodaju koraci, ลกto je i problem koji su generatori i usvojeni za rjeลกavanje.
  • ๐Ÿƒ Potreban trkaฤ: Generator ne pokreฤ‡e sam sebe; funkcija runner mora viลกe puta pozivati โ€‹โ€‹next da bi se asinhroni rad nastavio.
  • ๐Ÿฆฅ Lijeno izvrลกavanje: Obustavljanje i nastavak rada omoguฤ‡uje programu da povlaฤi vrijednosti samo u trenutku kada su potrebne.

Node.js GeneratorU usporedbi s povratnim pozivima

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.

  1. Metoda prinosa โ€” poziva se unutar funkcije za zaustavljanje izvrลกavanja na toฤno odreฤ‘enom retku yield pojavljuje.
  2. 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.

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 Objaลกnjenje

  1. Prvi korak je definirati funkciju generatora. Imajte na umu * dodano u function kljuฤna rijeฤ. Funkcija se poziva Add i uzima parametar x.
  2. The yield Kljuฤna rijeฤ je specifiฤna za generatore i pauzira funkciju usred izvrลกavanja. Izvrลกavanje se ovdje zaustavlja dok next() se poziva.
  3. Drugi yield pauzira funkciju i prima vrijednost: ลกto god se prosljeฤ‘uje sljedeฤ‡em next() poziv postaje vrijednost y.
  4. zvanje gen.next() prvi put izvrลกava funkciju do prvog yieldPozivanje gen.next(6) nastavlja ga, dodjeljuje 6 do yi vraฤ‡a se x + 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.

Povratni pozivi vs. Generators

function Timedelay(ptime, callback) {

    setTimeout(function() {

        callback("Pausing for " + ptime);

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

Code Objaลกnjenje

  1. Funkcija pod nazivom Timedelay stvara se s parametrom ptime, koji sadrลพi broj milisekundi za pauzu.
  2. 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.

Povratni pozivi vs. Generators

Timedelay(1000, function(message) {

    console.log(message);

    Timedelay(2000, function(message) {

        console.log(message);

        Timedelay(3000, function(message) {

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

Code Objaลกnjenje

  1. Timedelay poziva se kao povratni poziv s vrijednoลกฤ‡u 1000.
  2. Unutar tog povratnog poziva, Timedelay ponovno se poziva s 2000.
  3. Unutar tog, Timedelay se 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.

Povratni pozivi vs. Generators

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

Code Objaลกnjenje

  1. Generatorska funkcija je definirana za pozivanje Timedelay funkcija.
  2. yield koristi se sa Timedelay i 1000 kao vrijednost parametra.
  3. yield ponovno 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. yield vrijedi samo unutar function*.
  • Vrijednost iz next() stiลพe nedefinirana: prvi next() poziv ne moลพe isporuฤiti vrijednost jer generator nije dosegao yield joลก. Poลกaljite podatke od drugog poziva nadalje.
  • Beskonaฤna petlja u trkaฤu: result.done nikad se ne provjerava, pa next() 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.

Pitanja i odgovori

Objekt s dva svojstva: value, drลพeฤ‡i sve ลกto je predano, i done, logiฤka vrijednost koja postaje istinita nakon ลกto generator zavrลกi. Test trkaฤa se provodi kako bi se znalo kada stati.

Da. Generator koji sadrลพi beskonaฤnu petlju je siguran jer se niลกta ne izraฤunava dok se ne pozove next(). Zbog toga su generatori prikladni za nizove identifikatora ili paginirane rezultate.

Da. AI alati zamjenjuju yield s await i potpuno odbacuju runner. RevPaลพljivo pregledajte lijene sekvence, jer generator koji nikad ne zavrลกava nema izravni ekvivalent asinkronog ฤekanja.

AI asistenti trace je li trkaฤ sljedeฤ‡e prestao pozivati, je li povratni poziv za nastavak nikada nije pozvan ili je li dovrลกeno nikada nije dosegnuto. Ta tri ฤine gotovo svaki zaustavljeni generator.

Ne. Deklariran je asinkroni generator. async function* i konzumira se s for await...ofKombinira obje ideje i odgovara strujanju asinhronih podataka kao ลกto su kursori baze podataka.

Saลพmite ovu objavu uz: