Ok, so you have two yields and a return (which works basically the same but aways terminates the function). So you get to use next()
three times, then the done
property in the object that is updating each time flips to true and you’re finished. You are passing values in and as luck would have it the way JS evaluated things meant you ended up with values coming out instead of errors, but you’re getting results via luck not intent. I think that you think generators are like async/await. They aren’t. They’re useful in certain situations but aren’t generally used very much, I guess mainly because they way work seems a bit alien to most JS programmers.
In a sense, generators are functions that can be paused; they’re useful for building/iterating over streams of data, and let you write lazy functions that only act when requested to. But you can’t pause normal functions; once you return
that’s it. So to get round this, a generator function is used like a class constructor, it’s a factory, you can set it up, then call it to build iterator objects. By calling the next
method on that object, you get to the next step, denoted by yield
or return
. return
stops the function execution in a similar way to a normal function. But yield
returns the state at the time it is called, and can be used any number of times. You wouldn’t normally pass anything into the function once you’ve started it. Like a normal function, you give it the initial args then run it.
function * returnOnce(x) {
return x;
}
So, when you execute next()
with a function that returns, you get the value immediately and the done
value immediately goes to true.
> let ro = returnOnce('foo')
> ro.next()
Object { value: "foo", done: true }
> ro.next()
Object { value: undefined, done: true }
function * yieldOnce(x) {
yield x;
}
With yield
, it doesn’t go to done: true
straightaway, you need to call next again (as there could be any number of further yield
s).
> let yo = yieldOnce('foo')
undefined
> yo.next()
Object { value: "foo", done: false }
> yo.next()
Object { value: undefined, done: true }
Maybe you want a function that stops running after a set number of times?
function * yieldNTimes(n) {
let count = 0;
while (count < n) {
yield `Iteration number ${count + 1}`;
count++;
}
}
> let ynt = yieldNTimes(5);
> ynt.next()
Object { value: "Iteration number 1", done: false }
> ynt.next()
Object { value: "Iteration number 2", done: false }
> ynt.next()
Object { value: "Iteration number 3", done: false }
> ynt.next()
Object { value: "Iteration number 4", done: false }
> ynt.next()
Object { value: "Iteration number 5", done: false }
> ynt.next()
Object { value: undefined, done: true }
Or a function that runs an infinite number of times?
function * yieldRepeatedly() {
while (true) {
yield 'doing stuff';
}
}
(generators allow you to have functions that run indefinitely, in a safe manner)
> let yr = yieldRepeatedly();
undefined
> yr.next()
Object { value: "doing stuff", done: false }
> yr.next()
Object { value: "doing stuff", done: false }
> yr.next()
Object { value: "doing stuff", done: false }
> yr.next()
Object { value: "doing stuff", done: false }
...