I am using node 4.1.1. When I run this code
\"use strict\";
function *generator() {
let numbers = [1,2,3,4,5];
numbers.map(n => yield (n + 1));
}
f
Just discovered you can encounter this by accidentally closing your function too early.
i.e. one too many }
[1,2,3,4,5].map(function*(v){yield v+1;}).reduce((accumulator, currentValue) => accumulator = [...accumulator].concat([...currentValue]))
explanation...
[1,2,3,4,5].map(function*(v){yield v+1;})
pack all values into generator resulting
(5) [Generator, Generator, Generator, Generator, Generator]
unpack into flat array
.reduce((accumulator, currentValue) => accumulator = [...accumulator].concat([...currentValue]))
(5) [2, 3, 4, 5, 6]
for normal use
[1,2,3,4,5].map(function*(v){yield v+1;}).forEach(v => console.log([...v][0]))
2
3
4
5
6
[...v][0] is a bit ugly but it is works.
That's because the arrow function is not a generator. If I expand your arrow function, it would look something like:
function *generator() { // <-- this is your generator function
let numbers = [1,2,3,4,5];
numbers.map(function(n){ // <-- this one isn't a generator
yield (n + 1) // <-- there's your yield
}.bind(this));
}
You can do anything but not everything – Learn to delegate
Let's first look at two examples
1. yield
function* generator(numbers) {
yield numbers.map(x => x + 1);
}
for (let n of generator([1,2,3])) console.log(n);
// [ 2, 3, 4 ]
Our for
loop logs each value yielded by the generator. Inside our generator, we have a single yield
call which will yield the result of the numbers.map
call, which is a new Array. Because there is only a single yield, the only logged value is [2,3,4]
2. yield*
So yield
obviously won't work in the case above. We'll have to try something else.
function* generator(numbers) {
yield* numbers.map(x => x + 1);
}
for (let n of generator([1,2,3])) console.log(n);
// 2
// 3
// 4
Again, our for
loop logs each value yield
ed by the generator. Inside our generator, we yield the same result of the numbers.map
call, but this time we use yield*, which yield by delegation.
What are we yielding then? Well, Array's have a built-in generator, Array.prototype[Symbol.iterator]. So at this point, the for
loop is essentially directly stepping thru the generator provided by the Array. Since the array has 3 values, we see 3 logged values.
Watchful eyes
So we iterate thru numbers
once using Array.prototype.map
but then we iterate thru the intermediate array using the for
loop? Seems like a waste doesn't it?
Let's look back at your original code though
function *generator() {
let numbers = [1,2,3,4,5];
numbers.map(n => yield (n + 1));
}
for (var n of generator()) {
console.log(n);
}
Notice that your numbers.map
call is pretty meaningless. Array.prototype.map
creates a new array, but your generator doesn't do anything with it. So really you're just using map
to iterate thru the numbers, not because you actually care about the returned value of map
Say what you mean, mean what you say
OK, so now we know we only really care about iterating thru the numbers. So we'll use iteration the way JavaScript knows best
function* generator(numbers) {
for (let x of numbers)
yield x + 1
}
for (let n of generator([1,2,3])) console.log(n);
// 2
// 3
// 4
Bingo. No tricky yield*
. No double iteration. No nonsense.
It is because arrow functions are not generator functions. For example,
function temp() {
yield 1;
}
Can we expect this to work? No. Because temp
is not a generator function. The same is applicable to arrow functions as well.
FWIW, the usage of yield
in an Arrow function is an early error as per the ECMAScript 2015 specification, as per this section,
ArrowFunction : ArrowParameters => ConciseBody
It is a Syntax Error if ArrowParameters Contains YieldExpression is true.
It is a Syntax Error if ConciseBody Contains YieldExpression is true.