node.js Generators & Jämför med återuppringningar
⚡ Smart sammanfattning
Generators i Node.js är funktioner vars exekvering kan pausas och återupptas på begäran. Nyckelordet yield pausar funktionen, next-metoden återupptar den, och den parningen plattar ut djupt kapslade återanrop till läsbar sekventiell kod.

I den här genomgången tittar vi på generatorer och hur de skiljer sig från återanrop.
Vad är generatorer?
Generatorblev populärt i node.js på grund av vad de är kapabla att göra.
- Generators är funktionskörningar som kan avbrytas och återupptas vid en senare tidpunkt.
- Generators är användbara för koncept som "lat exekvering". Genom att pausa exekveringen och återuppta den när som helst hämtas värden endast när de behövs.
Generators har de två viktigaste metoderna nedan.
- Avkastningsmetod — anropas inuti en funktion för att stoppa exekveringen på exakt den rad där
yieldvisas. - Nästa metod — anropas från huvudapplikationen för att återuppta en funktion som har gett resultat. Körningen fortsätter till nästa
yield, eller till slutet av funktionen.
Låt oss titta på ett exempel på hur generatorer kan användas. Exemplet definierar en enkel Add-funktion som adderar två tal och stoppar exekveringen vid olika punkter för att visa hur generatorer beter sig.
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 Förklaring
- Det första steget är att definiera generatorfunktionen. Observera
*läggs till ifunctionnyckelord. Funktionen anropasAddoch tar en parameterx. - Ocuco-landskapet
yieldNyckelordet är specifikt för generatorer och pausar funktionen mitt i körningen. Körningen avbryts här tillsnext()åberopas. - Den andra
yieldbåde pausar funktionen och tar emot ett värde: vad som än skickas till följandenext()samtalet blir värdet avy. - ringa
gen.next()första gången körs funktionen upp till den förstayieldRingergen.next(6)återupptar det, tilldelar 6 tilly, och returnerarx + y.
⚠️ Rättelse: den ursprungliga listan innehöll raden y = 6 direkt efter var y = yield(null);Den tilldelningen skriver över vilket värde som helst next() leveranser, vilket motverkar syftet med exemplet och gör det andra yield meningslös. Värdet förs nu vidare genom gen.next(6), vilket är hur en generator faktiskt tar emot data från den som ringer.
Återuppringningar kontra generatorer
Generatorlöser problemet som kallas callback-helvete. Callback-funktioner blir ibland så djupt kapslade under utvecklingen av en Node.js-applikation att de är svåra att följa.
Det är här generatorer hjälper till. Ett av de vanligaste exemplen är att skapa timerfunktioner. Exemplet nedan bygger en enkel tidsfördröjningsfunktion och anropar den sedan med fördröjningar på 1000, 2000 och 3000 ms.
Steg 1) Definiera återuppringningsfunktionen med nödvändig tidsfördröjningskod.
function Timedelay(ptime, callback) { setTimeout(function() { callback("Pausing for " + ptime); }, ptime); // the delay must use ptime, the parameter name }
Code Förklaring
- En funktion som kallas
Timedelayskapas med en parameterptime, som anger antalet millisekunder att pausa i. setTimeoutväntar i så många millisekunder och anropar sedan den angivna återanropet.
⚠️ Rättelse: den ursprungliga koden som skickades time som det andra argumentet till setTimeout, men ingen variabel med namnet time finns — parametern är ptimeSom skrivet kastar det ReferenceError: time is not defined.
Steg 2) Betrakta nu koden med hjälp av återanrop. För att kedja fördröjningar på 1000, 2000 och 3000 millisekunder, kapslas återanropen inuti varandra.
Timedelay(1000, function(message) { console.log(message); Timedelay(2000, function(message) { console.log(message); Timedelay(3000, function(message) { console.log(message); }); }); });
Code Förklaring
Timedelayanropas som en återanrop med värdet 1000.- Inuti den återuppringningen,
Timedelayanropas igen med 2000. - Inuti den där,
Timedelaykallas en tredje gång med 3000.
⚠️ Rättelse: varje återanrop i den ursprungliga loggfilen msg, men parametern heter messageAlla tre linjerna kastade ReferenceError: msg is not defined.
Från koden ovan kan du se att det blir mer rörigt med varje ytterligare anrop — det är denna nestning som callback-helvetet beskriver.
Steg 3) Nu ser vi samma beteende skrivet med generatorer. Sekvensen läses som tre plana linjer snarare än tre nivåer av kapsling.
function* Messages() { console.log(yield Timedelay(1000)); console.log(yield Timedelay(2000)); console.log(yield Timedelay(3000)); }
Code Förklaring
- En generatorfunktion definieras för att anropa
Timedelayfunktion. yieldanvänds medTimedelayoch 1000 som parametervärde.yieldanvänds igen med 2000, och ytterligare en gång med 3000.
⚠️ Rättelse: originalet skrev console,log(...) med ett komma istället för en punkt på alla tre rader, vilket är ett syntaxfel och förhindrar att filen tolkas alls.
Hur man kör en Generator med en löparfunktion
Generatorn ovan är vackert läst men gör ingenting på egen hand. Det här är den detalj som de flesta förklaringar utelämnar, och det är anledningen till att den platta versionen kan se ut som magi. En generator är en pausad maskin, och något måste fortsätta trycka på knappen. Messages() skapar bara generatorobjektet och återvänder omedelbart; ingen timer startar någonsin och ingenting skrivs ut.
Den saknade delen är en runner: en liten funktion som anropar next(), väntar på att det asynkrona arbetet ska slutföras och anropar sedan 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 gör att detta fungerar. För det första, Timedelay tar inte längre emot en återanropning direkt; den returnerar en funktion som löparen kan anropa, vilket ger tillbaka kontrollen när timern utlöses. För det andra, it.next(value) matar in det föregående resultatet i den pausade generatorn, vilket är det som tillåter yield att visas på höger sida av en uppgift. För det tredje stannar löparen så snart som result.done blir sant, så rekursionen avslutas naturligt. Detta löparmönster är precis vad bibliotek som co tillhandahöll, och det är mekanismen som async/await senare absorberas i själva språket.
Generators vs async/await: Vilken ska man använda
Generators var standardsättet att skriva platt asynkron kod innan async/await existerade, och de två delar samma underliggande idé om att pausa en funktion mitt i. Node har stöttat async/await inbyggt sedan version 7.6, vilket ändrar rekommendationen för alla som börjar ett nytt arbete idag.
| Aspect | Generators | asynkronisera / vänta |
|---|---|---|
| Löpare behövs | Ja, skrivet av dig eller ett bibliotek | Nej, inbyggt i språket |
| Pausa sökord | yield |
await |
| Felhantering | Löparen måste vidarebefordra fel | Vanligt försök och fångst |
| Kan pausa halvvägs | Ja, och kan återupptas selektivt | Nej, körs till slut |
| Bäst för | Lata sekvenser och oändliga strömmar | Vanligt asynkront arbete |
Använda async/await för asynkront flöde i ny kod, eftersom det tar bort löparen och återställer vanliga försök och fångster. Behåll generatorer för det de bara gör bra: producera värden långsamt, iterera stora eller oändliga sekvenser, och alla fall där konsumenten bestämmer exakt när nästa värde beräknas.
Gemensam Generator Fel och hur man åtgärdar dem
De flesta generatorproblem beror på att man glömmer att funktionen är inert tills något anropar next(), eller från små syntaxrutor som glider runt asterisken. Varje symptom nedan anger dess orsak och dess lösning.
- Ingenting händer när generatorn anropas: Att anropa den returnerar en iterator, inte ett resultat.
next(), eller ge den till en löpare. - Syntaxfel vid avkastning: asterisken saknas.
yieldär endast giltigt inom enfunction*. - Värdet från next() anländer odefinierat: den första
next()anropet kan inte leverera ett värde, eftersom generatorn inte har nått ettyieldännu. Skicka data från och med det andra samtalet. - Oändlig loop i en runner:
result.donekontrolleras aldrig, sånext()anropas hela tiden efter slutet. Returnera alltid när `done` är sant. - Fel försvinner tyst: En avvisning inom det asynkrona steget skickas aldrig tillbaka till generatorn. Vidarebefordra den med
it.throw(err)så att en try and catch som är skriven inuti generatorn faktiskt kan se och hantera den.




