[ES6 knowledge] Generator function and yield keyword

1 Generator function

1.1 Overview

ES6 newly introduces the Generator function, which can suspend the execution flow of the function through the yield keyword, providing the possibility to change the execution flow, thus providing a solution for asynchronous programming.

Native JavaScriptScipt case collection
JavaScript + DOM basic
JavaScript basic to advanced
Canvas game development

Formally, the Generator function is an ordinary function, but it has two characteristics.

  1. There is an asterisk between the function keyword and the function name
  2. Yield expressions are used inside the function body to define different internal states (yield` means "output" in English).
function* func(){
 console.log("one");
 yield '1';
 console.log("two");
 yield '2'; 
 console.log("three");
 return '3';
}

Executing the Generator function will return a traverser object. That is to say, the Generator function is not only a state machine, but also a traverser object generation function. The returned traverser object can traverse each state inside the Generator function in turn.

yieldThere are two expressions (1 and 2) inside the Generator function above , that is, the function has three states: 1, 2 and return statement (end execution).

1.2 Execution mechanism

Calling the Generator function is the same as calling an ordinary function. Just add () after the function name. However, the Generator function will not be executed immediately like an ordinary function. Instead, it will return a pointer to the internal state object, so the iterator object Iterator must be called. Next method, the pointer will start execution from the head of the function or where it stopped last time.

f.next();
// one
// {value: "1", done: false}
 
f.next();
// two
// {value: "2", done: false}
 
f.next();
// three
// {value: "3", done: true}
 
f.next();
// {value: undefined, done: true}

When the next method is called for the first time, execution starts from the head of the Generator function. First, one is printed, and execution stops when yield is reached, and the value '1' of the expression after yield is used as the value attribute value of the returned object. This is When the function has not finished executing, the value of the done attribute of the returned object is false.
The same as the previous step when calling the next method for the second time.
When the next method is called for the third time, three is printed first, and then the return operation of the function is performed, and the value of the expression after return is used as the value attribute value of the returned object. At this time, the function has ended, and the done attribute value is usually used. is true.
When the next method is called for the fourth time, the function has finished executing, so the returned value attribute value is undefined and the done attribute value is true. If there is no return statement when executing the third step, {value: undefined, done: true} will be returned directly.

ES6 does not specify functionwhere the asterisk between the keyword and function name should be written. This causes the following writing methods to pass.

function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }

Since the Generator function is still an ordinary function, the general writing method is the third way above, that is, the asterisk follows the functionkeyword.

1.3 yield expression

1.3.1 Overview

Since the traverser object returned by the Generator function nextwill traverse the next internal state only by calling the method, it actually provides a function that can suspend execution. yieldThe expression is the pause flag.

The operation logic of the method of the traverser object nextis as follows:

(1) When yieldan expression is encountered, the execution of subsequent operations is suspended, and yieldthe value of the expression immediately following is used as the attribute value of the returned object value.

(2) The next time nextthe method is called, execution continues until the next yieldexpression is encountered.

(3) If no new yieldexpression is encountered, it will run until the end of the function until returnthe statement, and returnthe value of the expression after the statement will be used as the attribute value of the returned object value.

(4) If the function has no statement, the attribute value returnof the returned object is .valueundefined

It should be noted that yieldthe expression following the expression nextwill only be executed when the method is called and the internal pointer points to the statement. Therefore, it is equivalent to providing JavaScript with a manual "lazy evaluation" syntax function.

function* gen() {
  yield  123 + 456;
}

In the above code, yieldthe following expression 123 + 456will not be evaluated immediately. It will only be nextevaluated when the method moves the pointer to this sentence.

1.3.2 Similarities and differences yieldbetween expressions and statementsreturn

yieldreturnThere are similarities and differences between expressions and statements. The similarity is that both return the value of the expression immediately following the statement. The difference is that each time it is encountered yield, the function pauses execution and continues backward execution from that position next time, while returnthe statement does not have the function of position memory. In a function, a statement can only be executed once (or one) return, but an expression can be executed multiple times (or multiple) yield. A normal function can only return a single value because it can only be executed once return; a Generator function can return a range of values ​​because there can be any number of values yield. From another perspective, it can also be said that Generator generates a series of values, which is where its name comes from (in English, the word generator means "generator").

The Generator function does not need yieldexpressions, and then it becomes a simple suspended execution function.

function* f() {
  console.log('执行了!')
}

var generator = f();

setTimeout(function () {
  generator.next()
}, 2000);

In the above code, fif the function is an ordinary function, generatorit will be executed when assigning a value to a variable. However, the function fis a Generator function, so nextthe function will only be executed when the method is called f.

1.4 next() method parameters

yieldThe expression itself does not return a value, or it always returns undefined. nextThe method can take one parameter, which will be treated as yieldthe return value of the previous expression.

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 }

The above code first defines a Generator function that can run infinitely f. If the method has no parameters, the value of the variable will always be the same every time the expression nextis run . When a method takes a parameter , the variable is reset to this parameter (i.e. ), so it will be equal to , and the next cycle of the loop will be incremented from the beginning.yieldresetundefinednexttrueresettruei-1-1

This function has very important grammatical significance. When the Generator function resumes operation from the paused state, its context remains unchanged. Through nextthe parameters of the method, there is a way to continue injecting values ​​into the function body after the Generator function starts running. That is to say, different values ​​can be injected from the outside into the inside at different stages of the Generator function to adjust the function behavior.

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

In the above code, when the method is run for the second time nextwithout parameters, the value of y is equal to 2 * undefined(that is NaN), and it remains the same after dividing by 3 , so the properties NaNof the returned object are also equal to . When the method is run for the third time , there are no parameters, so it is equal to , and the properties of the returned object are equal to , that is . If you provide parameters to the method, the return result is completely different. When the above code calls the method for the first time, it returns the value ; when it calls the method for the second time , it sets the value of the previous expression to , so it is equal to , the value returned ; when it calls the method for the third time , it sets the value of the previous expression to Let's be , so equal to , then equal to , equal to , so the value of the statement is equal to .valueNaNNextzundefinedvalue5 + NaN + undefinedNaN
nextbnextx+16nextyield12y24y / 38nextyield13z13x5y24return42

Note that since nextthe method's parameters represent the return value of the previous expression, passing parameters is invalid yieldthe first time the method is used . The V8 engine directly ignores the parameters when the method nextis used for the first time . The parameters are only valid from the second time the method is used. Semantically speaking, the first method is used to start the traverser object, so no parameters are required.nextnextnext

Guess you like

Origin blog.csdn.net/qq_39335404/article/details/133375609
Recommended