En enkel guide för att förstå generatorer för Javascript (ES6)
Rajesh Babu
Följ
4 maj, 2018 – 9 min read
Förklaring:
När vi anropar den första next(20) skrivs varje kodrad fram till den första yield ut. Eftersom vi inte har något tidigare yield-uttryck kastas detta värde 20 bort. I utmatningen får vi yieldvärdet som i*10, vilket är 100 här. Dessutom stannar exekveringen med första yield och const j är ännu inte inställd.
Det andra anropet next(10) ersätter hela det första yield-uttrycket med 10, tänk dig yield (i * 10) = 10, vilket fortsätter med att ställa in värdet på const j till 50 innan det andra yield-värdet returneras. Yield-värdet här är 2 * 50 / 4 = 25.
Den tredje next(5), ersätter hela det andra yield-uttrycket med 5, vilket sätter värdet på k till 5. Och fortsätter vidare att utföra return statement och returnerar (x + y + z) => (10 + 50 + 5) = 65 som slutvärde tillsammans med done true.
Detta kan vara lite överväldigande för förstagångsläsare, men ta drygt 5 minuter på dig att läsa det om och om igen för att förstå helt och hållet.
Passing Yield as an Argument of a Function
Det finns n-antal användningsfall kring yield när det gäller hur det kan användas inuti en funktionsgenerator. Låt oss titta på koden nedan för en sådan intressant användning av yield, tillsammans med förklaringen.
Yield som argument för en funktion
Förklaring
Den första next() ger odefinierat värde eftersom yield-uttrycket saknar värde.
Den andra next() ger ”I am usless”, det värde som överlämnades. Och förbereder argumentet för funktionsanrop.
Den tredje next(), anropar funktionen med ett odefinierat argument. Som nämnts ovan innebär metoden next() som anropas utan argument i princip att hela det föregående yield-uttrycket är odefinierat. Därför skrivs detta ut odefinierat och avslutar körningen.
Yield med ett funktionsanrop
Avse att returnera värden kan yield också anropa funktioner och returnera värdet eller skriva ut detsamma. Låt oss titta på koden nedan för att förstå bättre.
Yield som anropar en funktion
Koden ovan returnerar funktionens retur obj som yield-värde. Och avslutar körningen genom att ställa in undefined till const user.
Yield med löften
Yield med löften följer samma tillvägagångssätt som funktionsanropet ovan, i stället för att returnera ett värde från funktionen returneras ett löfte som kan utvärderas vidare för att avgöra om det är lyckat eller misslyckat. Låt oss titta på koden nedan för att förstå hur det fungerar.
Yield med löften
ApiCall returnerar löftena som yield-värde, när de löses upp efter 2 sekunder skrivs det värde vi behöver ut.
Yield*
So långt har vi tittat på användningsområdena för yield-uttryck, nu ska vi titta på ett annat uttryck som kallas yield*. Yield* när det används inuti en generatorfunktion delegerar en annan generatorfunktion. Enkelt uttryckt avslutar det synkront generatorfunktionen i sitt uttryck innan det går vidare till nästa rad.
Låt oss titta på koden och förklaringen nedan för att förstå bättre. Koden kommer från MDN:s webbdokumentation.
Basic yield*
Förklaring
Det första anropet av next() ger ett värde på 1.
Det andra next() anropet är dock ett yield*-uttryck, vilket i sig innebär att vi kommer att slutföra en annan generatorfunktion som anges i yield*-uttrycket innan vi fortsätter den aktuella generatorfunktionen.
I ditt sinne kan du anta att koden ovan ersätts som den nedan
Detta kommer att fortsätta för att avsluta generatorkörningen. Det finns dock en distinkt förmåga hos yield* som du bör ha i åtanke när du använder return, i nästa avsnitt.
Yield* med return
Yield* med return beter sig lite annorlunda än den normala yield*. När yield* används med en retursats utvärderas den till det värdet, vilket innebär att hela yield*-funktionen() blir lika med det värde som returneras från den tillhörande generatorfunktionen.
Låt oss titta på koden och förklaringen nedan för att förstå den bättre.
Yield* med retur
Förklaring
I den första next() går vi direkt till yield 1 och returnerar dess värde.
Den andra next() ger 2.
Den tredje next(), returnerar ”foo” och går vidare till yield the ”the end” och tilldelar ”foo” till const-resultatet på vägen.
Den sista next() avslutar körningen.
Yield* med ett inbyggt iterbart objekt
Det finns ytterligare en intressant yield*-egenskap som är värd att nämna, i likhet med returvärdet kan yield* också iterera över iterbara objekt som Array, String och Map.
Låt oss titta på hur det fungerar i realtid.
Yield över inbyggda iterables
Här i koden itererar yield* över alla möjliga iterable objekt som skickas som sitt uttryck. Jag antar att själva koden är självförklarande.
Bästa praxis
Ovanpå allt detta kan varje iterator/generator itereras över en for…of-slinga. I likhet med vår next()-metod som anropas explicit går for…of-slingan internt vidare till nästa iteration baserat på nyckelordet yield. Och den itererar bara fram till den sista yield och behandlar inte return statements som next() metoden.
Du kan verifiera samma sak i koden nedan.
Yield med for…of
Det slutgiltiga returvärdet skrivs inte ut, eftersom for…of-slingan itererar bara fram till den sista yield. Så det är bästa praxis att undvika return statements i en generatorfunktion eftersom det skulle påverka funktionens återanvändbarhet när den itereras över en for…of-slinga.
Slutsats
Jag hoppas att det här täcker de grundläggande användningsområdena för generatorfunktioner och jag hoppas verkligen att det gav en bättre förståelse för hur generatorer fungerar i JavaScript ES6 och senare. Om du gillar mitt innehåll så lämna gärna 1, 2, 3 eller till och med 50 klappar :).
Följ mig på mitt GitHub-konto för fler JavaScript- och Full-Stack-projekt: