Generator notes

What is a generator?

Generators are an extremely flexible addition to ECMAScript 6, with the ability to pause and resume code execution within a function block.Copy the code

Declaration of generator functions

The form of a generator is a function whose name is preceded by an asterisk (*) to indicate that it is a generator. Wherever you can define a function, you can define a generator. * /

// Function declaration
function* show() {}// Function expression
let show = function* () {}// The function declaration in the object
let person = {
    *show(){}}// The function declaration in the class
class Person {*show(){}}// declared as a function in a static class
class Person {
    static *show(){}}// Note: arrow functions cannot be used to define generator functions
Copy the code

Basic use of generator functions

Calling a generator function produces a generator object.
// The generator object is initially paused.
// Generator objects implement the Iterator interface and therefore have the next() method.
// Calling the next() method causes the generator to start or resume execution.


The next() method returns a value similar to an iterator, with a done attribute and a value attribute. Generator functions with an empty body do not stop in the middle; a call to next() will bring the generator to the done: true state.
function* eat() {}const generator = eat();
console.log(generator.next());	//{ value: undefined, done: true }


The value attribute is the return value of the generator function. The default value is undefined, which can be specified by the return value of the generator function
function* hello() {
	return 'Hello Generator !!! ';
}
const generator = hello();
console.log(generator.next());	// { valeue: "Hello Generator !!!" , done: true }

// Note:
// Generator functions will only start executing after the first call to the next() method
// Generator objects implement the Iterable interface and their default iterators are self-referential
Copy the code

Interrupt execution by yield

  • The yield keyword allows the generator to stop and start execution, and is where the generator is most useful.
  • The generator function executes normally until the yield keyword is encountered.
  • Once the yield keyword is encountered, the execution is stopped and the state of the function’s scope is preserved.
  • A stopped generator function can only be resumed by calling the next() method on the generator object.
// The yield keyword is a bit like the intermediate return statement of a function, producing the value that will appear in the object returned by the next() method.
// A generator function that exits with yield will be in the done: false state.
// Exit generator functions with the return keyword in the done: true state.

function* eat() {
    yield "Breakfast!";
    yield "Lunch!;
    return 'Rest! ';
    yield "Dinner!;
}
const generator = eat();

console.log(generator.next());	// {value: "breakfast!" , done: false }
console.log(generator.next());	// {value: "lunch!" , done: false }
console.log(generator.next());	// {value: "rest!" , done: true }
console.log(generator.next());	// {value: undefined, done: true }

/ / note:
// The execution flow of the content part of a generator function is scoped for each generator object.
// Calling next() on one generator object does not affect other generators.
// The yield keyword can only be used inside a generator function and will throw an error if used elsewhere.
// It is invalid if nested non-generator functions occur, such as:
function* hello() {
    function item() {
        yield; }}const generator = hello();
generator.next();
// This is invalid
Copy the code

Generator objects can be used as iterators

// Because generator objects implement the Iterable interface, and because both generator functions and default iterators are called to produce iterators, generators are particularly suitable as default iterators.

/ / sample:
function* hello() {
    yield 'morning';
    yield 'lunch';
    yield 'the night';
}
for (let value of hello()) {
    console.log(value);
}
// Print the result:
/ / in the morning
/ / at noon
/ / in the evening
Copy the code

Use yield to implement input and output

In addition to being used as an intermediate return statement of a function, the yield keyword can also be used as an intermediate argument to a function.
// The value passed in here from the first call to next() will not be used because this call is to start the execution of the generator function

function* hello(value) {
    console.log(value);
    console.log(yield);
                console.log(yield);
    console.log(yield);
                }
const generator = hello('morning');
generator.next('The value here will not be used');
generator.next('lunch');
generator.next('the night');


// The yield keyword can be used for both input and output
function* hello(value) {
    return yield 'hello';
}
const generator = hello();
console.log(generator.next());				// { value: "hello", done: false }
console.log(generator.next('Generator'));	// { value: "Generator", done: true }

// Because the function must evaluate the entire expression to determine what value to return,
// So it pauses execution when it hits the yield keyword and calculates the value to produce
// The next call to next() passes in a value that is given to the same yield.
// This value is then determined as the value to be returned by the generator function
Copy the code

Produces an iterable

// You can use an asterisk to enhance the behavior of yield by iterating over an iterable, thus producing one value at a time

function* generatorFunc(value) {
    yield* [1.2.3];
}
const generator = generatorFunc();
console.log(generator.next());	// {value: 1, done: false}
console.log(generator.next());	// {value: 2, done: false}
console.log(generator.next());	// {value: 3, done: false}

Since yield* actually serializes an iterable into a sequence of values that can be produced individually, this is no different from putting yield into a loop.
Copy the code

Premature termination generator

// Like iterators, generators support the concept of "closeable".
// An object that implements the Iterable interface must have a next() method and an optional return() method to prematurely terminate the iterator.
In addition to these two methods, a generator object has a third method: throw().

// The return() and throw() methods can both be used to force the generator into a closed state.

1. return(a)// The return() method forces the generator into a closed state.
function* generatorFunc(value) {
    yield* [1.2.3];
}
const generator = generatorFunc();
console.log(generator.next());		// { value: 1, done: false }
console.log(generator.return());	// { value: undefined, done: true }
console.log(generator.next());		// { value: undefined, done: true }
// Unlike iterators, all generator objects have return() methods
// Once it enters the closed state, it cannot be recovered.
// A subsequent call to next() displays the done: true state, and any return value provided is not stored or propagated

2. throw(a)The // throw() method injects a supplied error into the generator object when paused. If the error is not handled, the generator closes
function* generatorFunc(value) {
    yield 1;
    try {
        throw new Error('Wrong');
    } catch (err) {
        console.log(err);
    }
    yield 2;
}
const generator = generatorFunc();
console.log(generator.next());
console.log(generator.next());
// However, if the generator function handles the error internally, the generator will not be shut down and execution can resume.
// Error handling skips the corresponding yield, so in this case a value will be skipped.
Copy the code