Generator is a kind of asynchronous programming solution proposed by ES6. It is completely different from traditional functions. This chapter explains and analyzes the basic concepts and usage. Before this, I was also in the fog of Generator function, so through this study, I hope to have a deeper understanding and understanding of Generator.

1. The concept

In ECMAScript 6, there are many ways to understand Generator functions. Syntactically, it can first be understood that a Generator function is a state machine that encapsulates multiple internal states.

Executing a Generator function returns a traverser object, that is, the Generator function is a traverser object Generator in addition to the state machine. The iterator object that is returned iterates through each state inside the Generator function in turn. The Generator has two characteristics: the first is to define the function keyword and the function name with an asterisk, and the second is to use the Yeild expression in the body of the function to represent different states.

function* foo() {
  yield "a";
  yield "b";
}
let f = foo();
console.log(f); Copy the code

Unlike normal functions, when this function is run, the return is not the result of the function, but rather an iterator object, or an object containing an internal state pointer. If you want the output to be worthwhile, use the next method to do it. The next method moves the pointer down, from the head of the function or from the last yield expression to the next yield expression (or until the return) each time the next method is called.

function* foo() {
  yield "a";
  yield "b";
}
let f = foo();
f.next(); // {value: "a", done: false} f.next(); // {value: "b", done: false} f.next(); // {value: undefined, done: true} Copy the code

As you can see above, when called next, the output is an object, that is, the value represents the result after yield, and the done indicates that the traversal is not finished. When the traversal is finished, the value is undefined and the done is true.

2. Yield expression

The lazy feature of yield is that the expression following yield is not executed unless the next method is called.

function* foo() {
  yield 1 + 1;
}
foo().next(); / / 2
Copy the code

Only when the next pointer is moved to this yield will the subsequent expression be executed. Yield and return are different. In Generator functions, multiple yield expressions can be defined, but only one return can be defined, and the yield traversal is not complete, but the traversal terminates when a return is encountered.

function* foo() {
  yield "a";
  yield "b";
  return "c";
}
let f = foo(); f.next(); // {value: "a", done: false} f.next(); // {value: "b", done: false} f.next(); // {value: "c", done: true} f.next(); // {value: undefined, done: true} Copy the code

When a return is encountered, the end of the traversal is done with true, value is the result after return, and the result after next is {value: undefined, done: true}. If there is no yield expression in a Generator function:

function* foo() {
  console.log("a");
}
let f = foo();
f.next();
Copy the code

Foo () still returns an object with an internal state pointer, which is executed only when the next method executes. When a yield is fused with another expression, enclose the yield expression in parentheses if the yield expression is on the left; otherwise, a syntax error will be reported.

function* foo() {
  console.log('a'+ yield 'b'); // Uncaught SyntaxError: Unexpected identifier
}
function* foo() {
  console.log('a'+ (yield 'b')); // {value: "b", done: false}
} let f = foo(); f.next(); Copy the code

When used as a function argument or placed to the right of an assignment expression, parentheses are not allowed.

function* demo() {
  foo(yield "a".yield "b"); // OK
  let input = yield; // OK
}
Copy the code

3. The next method

Yield has no return value. Its return value is undefined. We can pass the parameter to yield through the next method, and this parameter will be the return value of yield.

function* f() {
  for (var i = 0; true; i++) {
    var reset = yield i;
    if (reset) {
      i = - 1;
 }  } } var g = f(); g.next(); // { value: 0, done: false } g.next(); // { value: 1, done: false } g.next(true); // { value: 0, done: false } Copy the code

The above code defines a Generator function f that can run indefinitely. If the next method has no parameters, the value of the variable reset is always undefined each time the yield expression is run. When the next method takes an argument true, the variable reset is reset to that argument (true), so I will be equal to -1 and the next loop will start at -1. Can I print values without using the next method? The answer is yes, it can be used for… of… Method iterates through Generator functions.

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
 yield 5;  return 6; }  for (let v of foo()) {  console.log(v); // 1, 2, 3, 4, 5 } Copy the code

It is worth noting that… of… If done is true, the execution terminates, so 6 after return has no output.

4. Yield * expression

The yield* expression is used to solve the problem of calling a method provided by one Generator function in another.

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield* foo1();
} Equivalent;function* foo() {  yield 1;  yield 2;  yield 3;  for (let v of foo1()) {  yield v;  } } Copy the code

When there is no return in the Generator function following yield*, the function is used for… of… Iterating through the Generator function, returns the return value if there is a return in the Generator function.

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  var value = yield* foo1();
 console.log(value); } function* foo1() {  yield 4;  return 5; } let f = foo(); f.next(); // {value: 1, done: false} f.next(); // {value: 2, done: false} f.next(); // {value: 3, done: false} f.next(); // 5 {value: undefined, done: true} Copy the code

In fact, anything following yield* with an Iterator interface will be iterated.

function* foo() {
  yield* [1.2.3.4];
}
let f = foo();
f.next(); // {value: 1, done: false}
Copy the code

Here’s another example:

function* foo() {
  yield "a";
  yield "b";
  return "END";
}
function* bar(func) {  let result = yield* func();  console.log(result); } [...bar(foo)]; // END // ["a", "b"] Copy the code

In the example above, foo is a function that has a return expression, so the return result is’ assigned ‘to the yield* expression, so result is ‘END’, and the extension operator calls the Iterator interface by default, so result is printed first. Then yield is executed.

Reference:

Ruan Yifeng: Introduction to ECMAScript 6

After the language

Related articles:

  • Take you relearn ES6 | var, let and the difference between const
  • Take you relearn ES6 | Promsie
  • Take you relearn ES6 | Generator
  • Take you relearn ES6 | Async and Await
  • Take you relearn ES6 | Set and Map
  • Take you relearn ES6 | Symbol (more than just a new data type)
  • Take you relearn ES6 | Exprort (keep in mind that the output is variable)
  • Take you relearn ES6 | proxy and defineProperty

I think it’s ok, please give me a thumbs up when I leave, and we can study and discuss together!

You can also follow my blog and hope to give me a Start on Github, you will find a problem, almost all my user names are related to tomatoes, because I really like tomatoes ❤️!!

The guy who wants to follow the car without getting lost also hopes to follow the public account or scan the qr code below 👇👇👇.

I am a primary school student in the field of programming, your encouragement is the motivation for me to keep moving forward, 😄 hope to refueling together.

This article was typeset using MDNICE