The generator generator is a new data type introduced in the ES6 standard. A generator looks like a function but can return multiple times by using the yield keyword. It suspends the function's execution flow, providing the possibility to change the execution process and thus providing a solution for asynchronous programming.
Using the function* declaration defines a generator function, which returns a Generator object. It can be understood as a state machine encapsulating multiple internal states. Executing the generator function returns an iterator object.
Calling a generator function does not immediately execute the statements inside it; instead, it returns an iterator object that points to the internal state object. When the next() method of this iterator is called for the first time (subsequently), the statements inside it will execute up to the first (subsequent) appearance of yield, and the value following yield will be returned by the iterator. The pointer will then start executing from the beginning of the function or from the last suspended position to the next yield. If yield* is used, it means transferring the execution to another generator function (the current generator is suspended).
The next() method returns an object containing two properties: value, representing the return value of the current yield expression, and done, a boolean indicating whether there are subsequent yield statements in the generator function, i.e., whether the generator function has finished execution and returned.
function*f(x){yield x +10;yield x +20;return x +30;}var g =f(1);console.log(g);// f {<suspended>}console.log(g.next());// {value: 11, done: false}console.log(g.next());// {value: 21, done: false}console.log(g.next());// {value: 31, done: true}console.log(g.next());// {value: undefined, done: true} // It can go on indefinitely with next(), but value is always undefined and done is always true
When calling the next() method, if an argument is passed, it will be passed to the variable on the left of the last executed yield statement.
function*f(x){var y =yield x +10; console.log(y);yield x + y; console.log(x,y);return x +30;}var g =f(1);console.log(g);// f {<suspended>}console.log(g.next());// {value: 11, done: false}console.log(g.next(50));// {value: 51, done: false} // y is assigned the value 50console.log(g.next());// {value: 31, done: true} // x,y 1,50console.log(g.next());// {value: undefined, done: true}
If the return value for the return method is explicitly specified, that value is returned and the traversal of the generator function ends. If the return value for return is not explicitly specified, undefined is returned.
function*f(x){yield x +10;yield x +20;yield x +30;}var g =f(1);console.log(g);// f {<suspended>}console.log(g.next());// {value: 11, done: false}console.log(g.next());// {value: 21, done: false}console.log(g.next());// {value: 31, done: false} // Note that done here is falseconsole.log(g.next());// {value: undefined, done: true}
The yield* expression indicates that yield returns an iterator object, used within a Generator function to call another Generator function.
function*callee(){yield100;yield200;return300;}function*f(x){yield x +10;yield*callee();yield x +30;}var g =f(1);console.log(g);// f {<suspended>}console.log(g.next());// {value: 11, done: false}console.log(g.next());// {value: 100, done: false}console.log(g.next());// {value: 200, done: false}console.log(g.next());// {value: 31, done: false}console.log(g.next());// {value: undefined, done: true}