12

What's the difference between doing these two solutions to the same problem?

Closure Method

const numberIncrementer = startValue => () => startValue++ const getNextNumber = numberIncrementer(0) console.log(getNextNumber()) // 0 console.log(getNextNumber()) // 1 console.log(getNextNumber()) // 2 

Generator Method

const numberIncrementer = function*(startValue) { while(true) { yield startValue++ } } const numberFactory = numberIncrementer(0) const getNextNumber = () => numberFactory.next().value console.log(getNextNumber()) // 0 console.log(getNextNumber()) // 1 console.log(getNextNumber()) // 2 

Viewing these two methods, what reason do I have to choose one over the other?

11
  • Well the syntax is completely different? And try to do anything a bit more complex than an infinite loop… Commented Sep 8, 2017 at 15:33
  • 1
    There is tons of overlap in many APIs. In this example, the differences aren't really shown -- especially since it appears a closure is being created by the generator. The main differences come up in terms of async flow. While you have the .next() method hard coded into your getNextNumber function, it would perhaps more appropriately be outside of that function so you can call next() after some async work returns with data. But i could be wrong about all of this... Commented Sep 8, 2017 at 15:34
  • 1
    Functionally speaking, one big difference is that Generators produce Iterators, so you can use for..of to iterate over the data a generator produces. Another difference as that with the Generator you could process an infinite sequence. Commented Sep 8, 2017 at 15:39
  • 3
    Closures are sort of higher order functions and hence very powerful. It wouldn't surprise me, if they are equally expressive as generators in many scenarios. A closure provides state between function calls, which is stored on the heap and can consume (arguments) and produce (return) values. Sounds a lot like generators, at least technically. It's probably awkward in complex scenarios to maintain state like generators do, though. Commented Sep 8, 2017 at 18:15
  • 1
    To me it seems like comparing apples with oranges. Generators are basically syntactic sugar for creating iterators. Closures are functions that have free variables. Generator functions can be closures. Commented Sep 8, 2017 at 21:01

2 Answers 2

14

In the following contrived example I convert an Object into a Map without relying on an intermediate Array:

function* entries(o) { for (let k in o) yield [k, o[k]]; } const m = new Map(entries({foo: 1, bar: 2})) console.log( Array.from(m) // [["foo", 1], ["bar", 2]] );

This doesn't work with a closure, as you cannot access the state of the for/in loop to maintain it across function calls. The state is encapsulated within the loop construct. So there are cases that can be expressed with generators but not with closures.

Sign up to request clarification or add additional context in comments.

Comments

3

Adding to a previous answer: while there might be cases that can be expressed with generators but not with closures (I was looking for that answer as well), the one demonstrated by user6445533 is not one of them. Here is it, done with closures:

const entries = (o) => ({ entries: Object.entries(o), next() { return this.entries.length ? { value: this.entries.shift() } : { done: true }; }, [Symbol.iterator]() { return this; } }); const m = new Map(entries({foo: 1, bar: 2})); console.log(Array.from(m)); // [["foo", 1], ["bar", 2]]

So, maybe it's all syntactic sugar in the end?

2 Comments

Unless I'm mistaken, isn't using Symbol.iterator the same as creating a generator?
@KevinGhadyani By using Symbol.iterator your object is conforming to the iterable protocol, which generators must follow too. My point would be, a generator is just a closure conforming to a protocol, and the keywords function * and yield are only innovations in terms of syntax

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.