Et dypdykk i JavaScript generators

Et dypdykk i JavaScript generators

Generators er en ny primitiv som ble introdusert med ECMAScript 2015. Disse er kan hende ikke blitt adoptert i like stor grad som andre nyskapninger i språket, men denne bloggartikkelen skal forsøke å gi innblikk i hvordan bruk av generators kan forenkle koden din og gjøre det enklere å skrive asynkron kode.

En generator er en funksjon som produserer en sekvens av resultater i stedet for en enkelt verdi. I stedet for å returnere en verdi genererer du en serie med verdier med følgende struktur: 

{
  value: Any,
  done: Boolean
}

Verdien kan være hva som helst, en streng, en funksjon, et objekt og så videre, og done forteller oss om generatoren er ferdig med å produsere data. Sekvensen kan itereres over ved kall mot next.

Til forskjell fra en normal funksjon beholdes konteksten når en generator avgir en verdi.

Den blir bokmerket slik at den kan fortsette der den slapp. Når generatoren er ferdig sender den et tomt verdifelt og setter done til true

Fra lister til iterables

I JavaScript jobber vi vanligvis med lister når vi skal iterere over dataelementer. Utfordringen med lister er at de krever at data blir allokert før du kan jobbe med dem. Alle elementer i en liste og metadata om elementene må få plass et sted i minnet. Dette er en utfordring når du skal jobbe med svært store lister for du risikerer raskt å gå tom for både minne og datakraft.

Et alternativ til lister er sekvenser, som du kan tenke på som en form for liste som ikke allokerer plass i minnet, men som bare oppretter en peker til det nåværende elementet i listen. Tenk gjerne på generatorer som en mekanisme for å opprette sekvenser i JavaScript. 

function*

La oss opprette en generator og se hvordan de fungerer i praksis:

Legg merke til at generatoren ikke starter med en gang du oppretter den. Den starter først når du kaller på next.

Den vanlige bruksmåten for en generator er å avgi en verdi og kontrollen tilbake til den som kalte på generatoren, og så stå klar til å avgi en ny verdi fra der den fortsatt neste gang noen kaller på generatoren.  La oss se på en generator som har flere yields:

Vi har nå tre verdier som blir returnert for hvert kall på next() og verdien er den neste i generatorens kontekst. Vi har nå en ”lat” evaluering av datastrømmen. En liste ville gitt deg alle verdiene på en gang og overlatt til deg å iterere over dem, i stedet får vi bare neste verdi når vi spør etter den. Det er bare å være lat, for det gjør at vi ikke bruker mer datakraft og minne enn nødvendig.

Det er ikke ideelt at vi må kalle på console.log() tre ganger, når vi egentlig heller vil iterere over sekvensen. Dette er selvfølgelig mulig, så la oss se på par eksempler på hvordan vi gjør det:

Loopen for..of gjør det unødvendig å kalle på generator-konstruksjoner som next(), value og done. Dette er praktisk når vi har en begrenset generator som returner true for done på et tidspunkt, og hvor vi ønsker å jobbe med alle verdiene.

Et alternativ er å bruke while, slik som dette:

Hvordan gjør vi det når vi jobber med en uendelig generator, eller dersom vi bare ønsker å returnere noen av verdiene? Svaret er nok ganske åpenlyst, men la oss bare raskt se på hvordan vi kan gjøre det. Denne sekvensen returner true/false annenhver gang og kan for eksempel brukes til å generere en tabell med en bakgrunnsfarge for annenhver rad:

Dette er en veldig spesifikk generator som bare gir oss true eller false. Hva om vi vil lage en generator som lar oss sende inn hvilke verdier den skal alternere mellom? Dette kan vi gjøre ved å kombinere for..of loopen fra tidligere med ES2016 spread.

La oss se på enda et eksempel. Denne generatoren tar et stykke tekst, splitter den opp og forer den ut ord for ord ved hvert kall til next. Dette kan vi bruke til å presentere tekst stykkevis med intervaller slik at det ser ut som den skrives ut med skrivemaskin:

Generatorer er som vi har sett en ganske enkel konstruksjon, men det ganske enkle faktum at den kan beholde sin tilstand mellom kjøringer gjør den til et svært anvendelig og kraftig verktøy i verktøykassa. 

 

Om bloggeren:
Frontendutvikler med sans for JavaScript. Glad i å snakke om programmering, og har utgitt boken ReactJS Blueprints på Packt forlag.

comments powered by Disqus