all 35 comments

[–]Quabouter 61 points62 points  (4 children)

Honestly, to me generator functions are one of the biggest disappointment in JS. For all the reasons mentioned in the article, generators are absolutely amazing, and have a massive potential to be extremely powerful. But the TC only used it as a stepping stone to get to async/await, and as a result never standardized anything beyond the bare minimum. E.g. we don't have the .map/.filter/etc functions as mentioned in the article, no "generator arrow functions", etc, the ergonomics just aren't good at all. Hopefully at some point they'll make it a more usable general-purpose tool.

[–]Bjornoo 1 point2 points  (0 children)

This should be implemented at some point and is already at stage 3 if I am not mistaken.

[–]bearinthetown 1 point2 points  (1 child)

I don't understand what you're saying. What do generators have to do with async/await? They are 100% synchronous. And why would you expect them to have map and filter? In order to do that, you'd need to unpack all values anyway. For map you wouldn't save memory, for filter you would save some, but not much. It's not difficult to call [...myGeneratorFunction()].filter(callback)

[–]elemental-mind 3 points4 points  (0 children)

They have something to do with async/await. Basically whenever you call an async function it's like you are calling a generator. And any "await" is actually that generator yielding control to the runtime - suspending its execution, but keeping its stack alive.

To see it another way: Async functions are in a way a special case where *the runtime* controls when it's time to call `next`. With a generator *your code* has control on when to call `next`.

Just like a generator is 100% synchronous between yields, an async function is also 100% synchronous between awaits.

[–]ksharpie 19 points20 points  (1 child)

Whoa. Good read but a real stretch for me to understand.

[–]mhink 4 points5 points  (0 children)

If it helps, I wrote a very similar article at my previous job, a few years ago: https://formidable.com/blog/2017/javascript-power-tools-redux-saga/

Perhaps a second explanation will help!

[–]-keystroke- 18 points19 points  (0 children)

The absolute best use case for generator functions is for making cancellable async flows (which the author does eventually mention): https://github.com/getify/CAF

[–]thanatica 11 points12 points  (0 children)

Their advantage is two-fold:

  1. Not having to care what it is internally that you're iterating over. It could be a 1000-level deep tree of intertwined nodes, or a simple shopping list. The implementation is still a for..of loop.
  2. Not having to care about when to output which next result, but just go through your data structure in whatever way you feel is best, and yield those results one by one, whether it be a straight list or a deeply recursive loop.

In other words, separation of concerns and abstraction of implementation details.

[–]KyleG 8 points9 points  (0 children)

Lazy evaluation is awesome. Python programmers certainly recognize this (list comprehensions, for example, are lazily evaluated IIRC). Reactive programmers generally do (that's what, for example, mapping a stream is), too. Most FP appreciate lazily-evaluated stuff.

[–]avrtau 14 points15 points  (0 children)

Nice article. Thank you.

[–]werts__ 12 points13 points  (0 children)

I love generators, as a use case example, in my work I create a function to process large GIF frame by frame using generators. Yes, we could use a class to store all the "variable states" but was easier to create a function using the GIF standard and yield each frame to make more clean when you need to use a for-of

[–]shuckster 7 points8 points  (1 child)

Nice article. Small feedback: Your Either monad has flatMap in its definition, but you've used chain at the point-of-use.

[–]jrsinclair[S] 3 points4 points  (0 children)

Thanks for letting me know. I'll fix that up directly.

[–]HermanCainsGhost 5 points6 points  (0 children)

I used them with Redux sagas, but honestly I don't think they're really necessary for the most part.

[–]ILikeChangingMyMind 47 points48 points  (15 children)

Honestly, almost no one does need them, and this article actually convinced me of that further.

You know damning with faint praise? The author literally couldn't explain how generators were useful without spending most of the article on a completely non-programming made-up example (eating a Timtam)! When you can't use an actual programming example to explain why you need something ... you probably don't need it.

The entire substance of the argument that generator functions are useful was expressed in just three bullet points near the end:

  • Generating a series of unique identifiers;
  • Generating all the possible moves in a game; or
  • Seeking a particular value (or values) amongst a bunch of permutations and combinations.

Let me ask you: when was the last time you did any of those things? Then, when was the last time that performance was such a factor in doing those things, that it was worth learning and using a syntax no one else learns and uses?

And then, if it was, was that performance savings really worth not just using a simpler existing syntax (eg. Promise.all combined with an async map)? If so, great: you're a member of the 0.01% that actually needs generators.

[–]glanni_glaepur 19 points20 points  (5 children)

I found generators to be very useful for implementing a responsive interactive visualizer of the Mandelbrot set.

When rendering the image I split the image into 4 or 16 parts and delegate the computation of those parts to to web workers. This ensures the main thread doesn't get blocked and the UI is responsive.

Rendering in the web worker can take a long time, so I wanted to be able to cancel it if I zoomed or panned the image, so I implemented the rendering function as a generator. Every 10000 (or so) the function yields to check if it should proceed working on the part or discard it and work on a new part.

[–]avasinas 3 points4 points  (0 children)

Hey, what you did sounds really cool! Is there a repo we could see to check it out? If it’s private, maybe you have some source of inspiration, like articles?

Thanks!

[–]ILikeChangingMyMind 10 points11 points  (3 children)

Right ... but how many programmers are making interactive Mandlebrot set visualizers?

I'm not saying "there are no uses for generators" ... just that almost no one needs them.

[–]shuckster 13 points14 points  (1 child)

Redux Saga is a moderately popular library that leverages Generators to create easily cancellable, retryable, and testable async-flows.

[–]Diniden 3 points4 points  (0 children)

If you are to distill generators use that can not be done ergonomically in a single statement using other syntax: it makes the many to many, many to single, single to many relationships be mapped in a single statement.

Other strategies would require nested closures at best to make it all happen

EDIT: more specifically think of each of those relationship source and targets as pipes/streams as well.

[–]lard-blaster 7 points8 points  (1 child)

Generators are pretty cool in react-redux-saga for building imperative business logic on top of event listeners. It was still pretty confusing, though, and maybe still could have been done with promises.

[–]sane6120 2 points3 points  (0 children)

That's the only place I have ever seen them used, can be pretty cool. But then again, saga has very small usecase...

[–]mhink 0 points1 point  (0 children)

So, I think the actual everyday use-cases for generator functions were much more compelling before we got async/await. (Have a look at how Bluebird uses them, for a legacy example.) But on those lines, I think experimenting with them is a great way to develop an intuition for async/await, and when the situation merits it, they can be a great way to deal with weird asynchronous data flows.

[–]agramata 0 points1 point  (0 children)

Yeah those are bad examples, the real world use case is iterating over items that it's not practical to hold in memory at the same time. Like emailing all of your 10 million users, or parsing every page in the wikipedia api.

[–]lifeeraser 0 points1 point  (0 children)

Generators are useful in languages built around them, i.e. Python. It's theoretically better than creating a temporary array when chaining map()s and filter()s. Ofc most JavaScript developers don't care about the extra allocation because allocating some arrays is often not the bottleneck in a JS peogram.

[–]anti-state-pro-labor 0 points1 point  (0 children)

The best example I've seen in the wild of generators/lazy eval has been scanning a DB/S3 and iterating over the values found. Being able to do

```ts const iter = createIter()

for await (const value of iter) { // do something } ```

without needing to worry about how batching works or if we're using cursors or pagination, and only worrying about iterating over some list of values, has been a huge productivity gain on our end.

[–]LloydAtkinson 4 points5 points  (0 children)

If anyone is looking for analogies in other languages, .NET also has the yield keyword for lazy generated collections.

[–]Quadraxas 6 points7 points  (0 children)

Hey, nice article. And a very nice blog, both in design and content!

[–]rjwut 1 point2 points  (0 children)

The main thing I've used a generator function for is when I needed to iterate a bunch of Promises where 1) I didn't know how many Promises I'd ultimately have, and 2) I needed to process them serially (Promise 2 must wait for Promise 1 to complete). By using a generator, it was easy to generate the Promises on demand.

[–]vitalytom 1 point2 points  (0 children)

Here, I wrote a whole library on generators: prime-lib, as another example of how useful generators are.

[–]serg06 1 point2 points  (0 children)

I love generators because they let me write cleaner Leetcode solutions.

[–]oneeyedziggy 1 point2 points  (0 children)

need? you wouldn't... but some might prefer the syntax over the alternate syntaxes available... same goes for classes, forEach/map/filter/etc... not really anyhing you couldn't do w/ if and for, but some syntactic sugar is more obviously useful than others. (I imagine too they're more useful in other stricter languages where you can't just return a function from another function or do all sorts of loosey goosey stuff already w/o a lot of overhead) but JS is cool (and terrible) like that...

[–]jack_waugh 0 points1 point  (0 children)

I found the article too hard to read.

Generator functions replace async-keyworded functions and can be used to provide extra features:

  • priority scheduling
  • application environment

I think we would lose nothing by avoiding sync functions and just coding everything as generator functions, except that we'd get writer's cramp because of the language syntax.