ES6 Foundation: Generator

Generator generator

Generator is a peculiar function form, because its syntax behavior is completely different from traditional functions, and its frequency of use in ordinary projects is also very low. We often ignore it or even don't understand it. After all, being unfamiliar with it has little impact on daily development.

This article will not delve into its implementation principle, but hope to let you know what problems it was created to solve and what we can do with it?

1, Understanding generators

The generator can pause itself during execution, resume execution immediately, or resume execution after a period of time. So obviously, it doesn't guarantee to run to the end like ordinary functions.

1.1 characteristics

The generator is a state management machine. Several states can be saved internally, and manual triggering is required between states.

Simple understanding: it is a special function, which contains two characteristics:

  • There is an asterisk between the function keyword and the function name;
  • Inside the function body, the yield expression is used to define different internal states (yield means "output" in English).

1.2 running the first generator

function* generatorOne() {
  yield 1;
  return 2;
}

const g = generatorOne(); // Generator {<suspended>}
g.next(); // {value: 1, done: false}
g.next(); // {value: 2, done: true}

be careful:

  1. function *foo() {...} , function* foo() {...} , function * foo() {...} All generator declarations
  2. Although the generator is declared with *, it performs the same as ordinary functions:

2, yield keyword

There is a keyword yield in the generator, which is the flag for the stop of state switching.

2.1 operation logic

  • Execute next() every time
    1. When a yield expression is encountered, the following operations are suspended. The value of the expression immediately followed by yield is used as the value attribute value of the returned object;
    2. If no yield expression is encountered, it runs until the return statement, and takes the value of the expression after the return statement as the value attribute value of the returned object;
    3. If the function does not have a return statement, the value attribute value of the returned object is undefined.

It should be noted that the yield expression will be executed only when the next method is called and the internal pointer points to the statement. Therefore, it provides the syntax function of manual Lazy Evaluation for JavaScript.

2.2 actual use

Combined with the text description in the previous section, let's analyze it step by step

function* generatorSleep(a, b) {
  console.log(1);
  console.log(2);
  yield;
  console.log(a + b);
  return "end";
}
const i = generatorSleep(100, 200); // Create generator

// Consume once, pause in the first yield expression, and return the value of the expression to the value attribute of the value object
i.next();
// Output: 1, 2
// Return: {value: undefined, done: false}

// Consume once without new yield, encounter return, and return the value of the expression to the value attribute of the value object
i.next();
// Return: {value: 'end', done: false}

Students who read the results in the last line above may have questions. Why is value undefiend for the first time?

Because: immediately after yield, there is no expression and no return value. Naturally, value is undefined.

An infinite iterator

function* foo() {
  let index = 1;
  while (true) {
    yield index++;
  }
}

const i = foo();
console.log(i.next().value); // 1
console.log(i.next().value); // 2
console.log(i.next().value); // 3
console.log(i.next().value); // 4

The above is a while... true loop in the generator. Each iteration will yield, and the expression will get the result of index. It represents the number of executions.

3, Method

3.1 next()

Calling next() is the only way to change the state

Returns the value generated by a yield expression. It is an object that contains the attributes done and value. This method can also pass a value to the generator by accepting a parameter.

Here are the most common ways to use it:

function* generator() {
  const a = yield 1;
  const b = yield 2;
  return "It's over";
}
const i = generator(); // Generator
i.next(); // {value: 1, done: false}
i.next(); // {value: 2, done: false}
// At this point, done changes to true
i.next(); // {value: 'end', done: true}

The next method can have parameters

Many students will be concerned about the following code, var x = yield 10;, What would X be?

function* foo() {
  var x = yield 10;
  console.log(x);
}

const i = foo();
i.next(); // {value: 10, done: false}
i.next();
// undefined
// {value: undefined, done: true}

The value of x is undefined, which means that yield 10 itself has no return value. To give it a return value, we can do this

function* gen() {
  while (true) {
    var value = yield null;
    console.log(value);
  }
}

var g = gen();
g.next(1);
// "{ value: null, done: false }"
g.next(2);
// 2
// "{ value: null, done: false }"

3.2 method return()

The following example shows the use of a simple generator and return method.

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next(); // { value: 1, done: false }
g.return("foo"); // { value: "foo", done: true }
g.next(); // { value: undefined, done: true }

be careful:

  • If you call return(value) on a generator that is already in the completed state, the generator will remain in the completed state.
  • If no parameter is provided, the value attribute of the object and the last value of the example are returned The next() method is the same.
  • If a parameter is provided, the parameter is set to return the value of the value property of the object.

For example:

function* gen() {
  yield 1;
  yield 2;
}

var g = gen();
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: undefined, done: true }
g.return(); // { value: undefined, done: true }
g.return(1); // { value: 1, done: true }

3.3 throw()

The throw() method is used to throw an exception to the generator, resume the execution of the generator, and return an object with the attributes of done and value.

function* gen() {
  while (true) {
    try {
      yield 42;
    } catch (e) {
      console.log(e);
    }
  }
}

var g = gen();
g.next(); // { value: 42, done: false }
g.throw(new Error("Something went wrong")); // Error: Something went wrong

Write at the end

  • Flower name: Yu Guang
  • WX: j565017805
  • Email: webbj97@163.com

Other precipitation

Keywords: ECMAScript

Added by crash4o4 on Wed, 26 Jan 2022 05:33:46 +0200