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?
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?
Share Improve this question edited Sep 8, 2017 at 15:28 Kevin Ghadyani asked Sep 8, 2017 at 15:22 Kevin GhadyaniKevin Ghadyani 7,2978 gold badges47 silver badges66 bronze badges 11- Well the syntax is pletely different? And try to do anything a bit more plex than an infinite loop… – Bergi 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 e up in terms of async flow. While you have the
.next()
method hard coded into yourgetNextNumber
function, it would perhaps more appropriately be outside of that function so you can callnext()
after someasync
work returns with data. But i could be wrong about all of this... – Pytth Commented Sep 8, 2017 at 15:34 -
1
Functionally speaking, one big difference is that Generators produce
Iterators
, so you can usefor..of
to iterate over the data a generator produces. Another difference as that with the Generator you could process an infinite sequence. – Rob M. 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 plex scenarios to maintain state like generators do, though. – user6445533 Commented Sep 8, 2017 at 18:15
- 1 To me it seems like paring apples with oranges. Generators are basically syntactic sugar for creating iterators. Closures are functions that have free variables. Generator functions can be closures. – Felix Kling Commented Sep 8, 2017 at 21:01
2 Answers
Reset to default 14In 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.
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?