DEV Community

Cover image for Generators in Javascript: How to use them

Generators in Javascript: How to use them

Karim Elghamry on February 05, 2021

Opening Note Hello fellow programmers 👋 In this article, we will walk-through the basics of generators in Javascript, which was introduc...
Collapse
 
alimobasheri profile image
MirAli Mobasheri • Edited

Nice article! You described everything very well.

Collapse
 
karimelghamry profile image
Karim Elghamry

Thank you! 😁 I tried to encapsulate the majority of the concepts into one article, glad it went well.

Collapse
 
tassoevan profile image
Tasso Evangelista • Edited

It's a shame that generators kinda look terrible in TypeScript.

const output = yield input;
Enter fullscreen mode Exit fullscreen mode

It's impossible to properly type output based on input. You've to use a wide type for both and force type narrowing by hand.

Collapse
 
tylors profile image
Tylor Steinberger

If you switch to yield * the type do work surprisingly well.

For example, github.com/briancavalier/fx-ts

Collapse
 
tassoevan profile image
Tasso Evangelista

Hmmm, I haven't wondered it! It surely restricts a little what you can express in code, but I not sure about what is lost.

Thread Thread
 
tylors profile image
Tylor Steinberger

Thankfully it shouldn't! There are still yields happening under the hood, but in Generator<Yield, Return, Next>, Next is the return value of individual yields which there can be many (source of the issue is they must be a product type A & B), while Return is the value returned when .next() returns done:true, and since there is only 1 value there we don't have to worry about the same typing complexities.

It's very easy to convert from one to the other. function*(thing) { const x = yield thing; return x}

Collapse
 
slikts profile image
Reinis Ivanovs

An important omission about use cases is that generators are a relatively low-level tool, so they're relevant to, for example, library authors, but not for most language users.

Collapse
 
karimelghamry profile image
Karim Elghamry

That's true to some extent. Generators are not meant to be used on a day-to-day basis and you can actually develop solutions to your problem without needing to touch generators. Nevertheless, it's a new feature added to the table, so it's good to be aware of their existence while developing your own solutions. I've tried to include a couple of basic use cases that might see the usage of generators.

Collapse
 
slikts profile image
Reinis Ivanovs

Sorry, but you should have qualified the use cases as being for, for example, library authors, because this way they're bound to be misapplied and waste time.

Thread Thread
 
karimelghamry profile image
Karim Elghamry

I don't see anything bound to library authors or a specific category of users in the basic use cases that I've provided above, especially the visualization use case. Also, you should've read my closing note more carefully as it states that generators are not the best solution for most problems. As any other existing approach, you must always think of the pros and cons of it before applying it to the problem in-hand.

Thread Thread
 
designbyonyx profile image
Ryan Wheale

I think like anything else it's another tool in the tool belt. Sometimes the best way to learn is to pull the wrong tool, or build your own version of a tool instead of bringing in an entire tool shed like lodash. While some might consider that "wasted" time, I consider it educational time.

Collapse
 
kgilpin profile image
Kevin Gilpin

To me, being able to traverse large data structures without having to load them all in memory, and without having to provide a callback-based interface, seems really useful. Useful enough that it can make its way into a lot of code. Maybe not for webdev stuff like vue/react coding, but for pretty hard core data processing like crunching through large data files? Yes!

Collapse
 
lcfvs profile image
Lcf.vs

Better distinctRandom:

function* distinctRandom({limit = 10}) {
  // we create an array that contains all numbers in range [0:limit)
  // this is our initial pool of numbers to choose from
  const availableValues = [...new Array(limit).keys()];

  // we repeatedly loop until the available pool of numbers is empty
  while (availableValues.length !== 0) {
    // generate a random index in range [0: availableValues.length)
    // then, yield the number that is present at the chosen index
    // Finally, remove the picked item from the pool of available numbers
    const currentRandom = Math.floor(Math.random() * availableValues.length);
    yield availableValues.splice(currentRandom, 1)[0];
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
maxwelloroark profile image
Sp00kyCat

I was trying to think up a use-case for a front end web app. Possibly... a series of modal dialogues? Hook up a generator to a button click listener on a "delete" button and then yield "Are you sure you want to delete?" "Are you sure you're sure", final yield, "Okay deleting thing".

Collapse
 
maruffahmed profile image
Maruf Ahmed

Thanks, you explain the JS generator to me. ❤

Collapse
 
karimelghamry profile image
Karim Elghamry • Edited

I'm glad that I was able to help! 😁🎉

Collapse
 
skflowne profile image
skflowne

You did a great job of explaining this simply and efficiently

Collapse
 
spyke profile image
Anton Alexandrenok

If you like functional style and generators, you may find the Undercut handy. The Pull is basically native generators + iterators.

Collapse
 
vrohit profile image
rohitv-dev

Is that range generator performant enough, compared to the traditional for loop? Cause there are two for loops going on right? I am curious.

Collapse
 
destroyer22719 profile image
Nathan Cai

isn't this pretty similar to async/await?

Collapse
 
karimelghamry profile image
Karim Elghamry • Edited

There are some key differences. One of the biggest differences is that after finishing the await call inside an async function, the function does not terminate nor return a value. Instead, it continues its execution normally. With generators however, you can pause and return values on demand using the yield keyword.

But if you actually think about it, they have so much in common. Actually, it is easy to replicate the behavior of async/await using generators. This article describes a brief idea on how to do this with little effort.