node.js Generators & Sammenlign med tilbagekald

โšก Smart opsummering

Generator`s` i Node.js er funktioner, hvis udfรธrelse kan suspenderes og genoptages efter behov. `yield`-nรธgleordet sรฆtter funktionen pรฅ pause, `next`-metoden genoptager den, og denne parring flader dybt indlejrede callbacks ud til lรฆsbar sekventiel kode.

  • โธ๏ธ Kerneadfรฆrd: En generator stopper ved hvert udbytte og genoptager prรฆcis, hvor den stoppede, nรฅr den nรฆste kaldes.
  • โœณ๏ธ Erklรฆring: En asterisk efter funktionsnรธgleordet markerer funktionen som en generator i stedet for en almindelig en.
  • ๐Ÿ”„ Tovejskanal: En vรฆrdi, der sendes til next, bliver resultatet af det yield-udtryk, der satte funktionen pรฅ pause.
  • ๐Ÿช† Tilbagekaldelseshelvede: Indlejrede callbacks bliver ulรฆselige, efterhรฅnden som der tilfรธjes trin, hvilket er de problemgeneratorer, som blev anvendt til at lรธse.
  • ๐Ÿƒ Lรธber krรฆves: En generator driver ikke sig selv; en runner-funktion skal kalde next gentagne gange for at asynkront arbejde kan fortsรฆtte.
  • ๐Ÿฆฅ Doven udfรธrelse: Afbrydelse og genoptagelse lader et program kun hente vรฆrdier i det รธjeblik, de er nรธdvendige.

node.js GeneratorSammenlignet med tilbagekald

I denne gennemgang ser vi pรฅ generatorer og hvordan de adskiller sig fra callbacks.

Hvad er generatorer?

Generatorblev populรฆr i node.js pรฅ grund af hvad de er i stand til at gรธre.

  • Generators er funktionsudfรธrelser, der kan suspenderes og genoptages pรฅ et senere tidspunkt.
  • Generators er nyttige til koncepter som "doven udfรธrelse". Ved at suspendere udfรธrelsen og genoptage den efter behov, hentes vรฆrdier kun, nรฅr der er behov for dem.

Generators har de to vigtigste metoder nedenfor.

  1. Udbyttemetode โ€” kaldes inde i en funktion for at stoppe udfรธrelsen pรฅ den prรฆcise linje, hvor yield kommer til syne.
  2. Nรฆste metode โ€” kaldet fra hovedapplikationen for at genoptage en funktion, der har givet afkald. Udfรธrelsen fortsรฆtter indtil den nรฆste yield, eller indtil funktionen er slut.

Lad os se pรฅ et eksempel pรฅ, hvordan generatorer kan bruges. Eksemplet definerer en simpel Add-funktion, der lรฆgger to tal sammen og stopper udfรธrelsen pรฅ forskellige punkter for at vise, hvordan generatorer opfรธrer sig.

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 Forklaring

  1. Det fรธrste trin er at definere generatorfunktionen. Bemรฆrk * tilfรธjet til function nรธgleord. Funktionen kaldes Add og tager en parameter x.
  2. yield Nรธgleordet er specifikt for generatorer og sรฆtter funktionen pรฅ pause midt i udfรธrelsen. Udfรธrelsen stopper her indtil next() pรฅkaldes.
  3. Den anden yield bรฅde sรฆtter funktionen pรฅ pause og modtager en vรฆrdi: hvad der end sendes til fรธlgende next() opkaldet bliver vรฆrdien af y.
  4. Opkald gen.next() fรธrste gang kรธrer funktionen op til den fรธrste yieldOpkald gen.next(6) genoptager det, tildeler 6 til y, og returnerer x + y.

โš ๏ธ Rettelse: Den oprindelige liste indeholdt linjen y = 6 umiddelbart efter var y = yield(null);Den tildeling overskriver enhver vรฆrdi next() forsyninger, hvilket modvirker formรฅlet med eksemplet og gรธr det andet yield meningslรธs. Vรฆrdien gives nu videre gennem gen.next(6), hvilket er sรฅdan en generator rent faktisk modtager data fra den, der ringer op.

Tilbagekald vs. generatorer

Generators lรธser problemet kendt som callback-helvede. Callback-funktioner bliver nogle gange sรฅ dybt indlejrede under udviklingen af โ€‹โ€‹en Node.js-applikation, at de er vanskelige at fรธlge.

Det er her, generatorer hjรฆlper. Et af de mest almindelige eksempler er oprettelse af timerfunktioner. Eksemplet nedenfor bygger en simpel tidsforsinkelsesfunktion og kalder den derefter med forsinkelser pรฅ 1000, 2000 og 3000 ms.

Trin 1) Definer callback-funktionen med den nรธdvendige tidsforsinkelseskode.

Tilbagekald vs. Generators

function Timedelay(ptime, callback) {

    setTimeout(function() {

        callback("Pausing for " + ptime);

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

Code Forklaring

  1. En funktion kaldet Timedelay er oprettet med en parameter ptime, som angiver antallet af millisekunder, der skal holdes pause i.
  2. setTimeout venter i det antal millisekunder og kalder derefter det angivne callback.

โš ๏ธ Rettelse: den oprindelige kode, der blev bestรฅet time som det andet argument til setTimeout, men ingen variabel navngivet time findes โ€” parameteren er ptimeSom skrevet kaster det ReferenceError: time is not defined.

Trin 2) Overvej nu koden ved hjรฆlp af callbacks. For at kรฆde forsinkelser pรฅ 1000, 2000 og 3000 millisekunder sammen, skal callbacks indlejres inden i hinanden.

Tilbagekald vs. Generators

Timedelay(1000, function(message) {

    console.log(message);

    Timedelay(2000, function(message) {

        console.log(message);

        Timedelay(3000, function(message) {

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

Code Forklaring

  1. Timedelay kaldes som et callback med vรฆrdien 1000.
  2. Inde i det tilbagekald, Timedelay kaldes igen med 2000.
  3. Inde i den ene, Timedelay kaldes en tredje gang med 3000.

โš ๏ธ Rettelse: hvert tilbagekald i den oprindelige logfรธring msg, men parameteren er navngivet messageAlle tre linjer kastede ReferenceError: msg is not defined.

Fra koden ovenfor kan du se, at det bliver mere rodet med hvert yderligere kald โ€” denne indlejring er, hvad callback-helvede beskriver.

Trin 3) Se nu den samme opfรธrsel skrevet med generatorer. Sekvensen lรฆses som tre flade linjer i stedet for tre niveauer af indlejring.

Tilbagekald vs. Generators

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

Code Forklaring

  1. En generatorfunktion er defineret til at kalde Timedelay funktion.
  2. yield bruges med Timedelay og 1000 som parametervรฆrdi.
  3. yield bruges igen med 2000, og endnu en gang med 3000.

โš ๏ธ Rettelse: originalen skrev console,log(...) med et komma i stedet for et punktum pรฅ alle tre linjer, hvilket er en syntaksfejl og forhindrer filen i at blive parset overhovedet.

Sรฅdan kรธrer du en Generator med en lรธberfunktion

Generatoren ovenfor er smukt lรฆsbar, men kan ikke gรธre noget i sig selv. Dette er den detalje, de fleste forklaringer udelader, og det er grunden til, at den flade version kan ligne magi. En generator er en maskine, der er sat pรฅ pause, og noget skal blive ved med at trykke pรฅ knappen. Opkald Messages() opretter blot generatorobjektet og vender tilbage med det samme; ingen timer starter nogensinde, og intet udskrives.

Den manglende brik er en lรธber: en lille funktion, der kalder next(), venter pรฅ, at det asynkrone arbejde er fรฆrdigt, og kalder derefter next() igen med resultatet.

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

Tre punkter fรฅr dette til at fungere. For det fรธrste, Timedelay tager ikke lรฆngere et callback direkte; den returnerer en funktion, som lรธberen kan kalde, og giver kontrollen tilbage, nรฅr timeren aktiveres. For det andet, it.next(value) fรธder det forrige resultat ind i den pausede generator, hvilket er det, der tillader yield at vises pรฅ hรธjre side af en opgave. For det tredje stopper lรธberen, sรฅ snart result.done bliver sand, sรฅ rekursionen afsluttes naturligt. Dette lรธbermรธnster er prรฆcis, hvad biblioteker som co leverede, og det er den mekanisme, der async/await senere absorberet i selve sproget.

Generators vs async/await: Hvilken skal bruges

Generators var standardmรฅden til at skrive flad asynkron kode fรธr async/await eksisterede, og de to deler den samme underliggende idรฉ om at sรฆtte en funktion pรฅ pause midtvejs. Node har understรธttet async/await indbygget siden version 7.6, hvilket รฆndrer anbefalingen for alle, der starter nyt arbejde i dag.

Aspect Generators asynkron/afvent
Lรธber sรธges Ja, skrevet af dig eller et bibliotek Nej, indbygget i sproget
Sรฆt sรธgeord pรฅ pause yield await
Fejl ved hรฅndtering Lรธber skal videresende fejl Almindelig forsรธg og fangst
Kan holde pause midtvejs Ja, og kan genoptages selektivt Nej, kรธrer til fรฆrdiggรธrelse
Bedste for Dovne sekvenser og uendelige strรธmme Almindeligt asynkront arbejde

Brug async/await for asynkront flow i ny kode, da det fjerner runneren og gendanner almindelig try and catch. Behold generatorer til det, de kun gรธr godt: dovent producere vรฆrdier, iterere store eller uendelige sekvenser og ethvert tilfรฆlde, hvor forbrugeren bestemmer prรฆcis, hvornรฅr den nรฆste vรฆrdi beregnes.

Fรฆlles Generator Fejl og hvordan man retter dem

De fleste generatorproblemer skyldes, at man glemmer, at funktionen er inaktiv, indtil noget kalder next(), eller fra smรฅ syntakser, der glider rundt om asterisken. Hvert symptom nedenfor angiver dets รฅrsag og dets lรธsning.

  • Der sker ikke noget, nรฅr generatoren kaldes: Et kald af den returnerer en iterator, ikke et resultat. next(), eller giv den videre til en lรธber.
  • Syntaksfejl ved udbytte: asterisken mangler. yield er kun gyldig indenfor en function*.
  • Vรฆrdien fra next() ankommer udefineret: den fรธrste next() opkaldet kan ikke levere en vรฆrdi, fordi generatoren ikke har nรฅet en yield endnu. Send data fra det andet opkald og fremefter.
  • Uendelig lรธkke i en runner: result.done bliver aldrig tjekket, sรฅ next() bliver ved med at blive kaldt efter slutningen. Returner altid nรฅr fรฆrdig er sandt.
  • Fejl forsvinder lydlรธst: En afvisning i det asynkrone trin sendes aldrig tilbage til generatoren. Send den videre med it.throw(err) sรฅ en "try and catch" skrevet inde i generatoren rent faktisk kan se og hรฅndtere den.

Ofte Stillede Spรธrgsmรฅl

Et objekt med to egenskaber: value, idet han holdt det, der blev afleveret, og done, en boolsk vรฆrdi, der bliver sand, nรฅr generatoren er fรฆrdig. Lรธbere er testet for at vide, hvornรฅr de skal stoppe.

Ja. En generator, der indeholder et endelรธst loop, er sikker, fordi intet beregnes, fรธr next() kaldes. Dette gรธr generatorer velegnede til strรธmme af identifikatorer eller paginerede resultater.

Ja. AI Vรฆrktรธjer bytter yield med await og dropper runneren helt. RevSe dovne sekvenser omhyggeligt, fordi en generator, der aldrig afslutter, ikke har nogen direkte asynkron afventningsรฆkvivalent.

AI-assistenter trace om lรธberen stoppede med at kalde det nรฆste, om et genoptagelseskald aldrig blev kaldt, eller om fรฆrdig aldrig nรฅs. Disse tre tegner sig for nรฆsten alle generatorer, der er gรฅet i stรฅ.

Nej. En asynkron generator er deklareret async function* og forbruges med for await...ofDet kombinerer begge ideer og er velegnet til streaming af asynkrone data, sรฅsom databasemarkรธrer.

Opsummer dette indlรฆg med: