Article directory
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.
- There is an asterisk between the function keyword and the function name
- 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.
yield
There 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 function
where 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 function
keyword.
1.3 yield expression
1.3.1 Overview
Since the traverser object returned by the Generator function next
will traverse the next internal state only by calling the method, it actually provides a function that can suspend execution. yield
The expression is the pause flag.
The operation logic of the method of the traverser object next
is as follows:
(1) When yield
an expression is encountered, the execution of subsequent operations is suspended, and yield
the value of the expression immediately following is used as the attribute value of the returned object value
.
(2) The next time next
the method is called, execution continues until the next yield
expression is encountered.
(3) If no new yield
expression is encountered, it will run until the end of the function until return
the statement, and return
the 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 return
of the returned object is .value
undefined
It should be noted that yield
the expression following the expression next
will 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, yield
the following expression 123 + 456
will not be evaluated immediately. It will only be next
evaluated when the method moves the pointer to this sentence.
1.3.2 Similarities and differences yield
between expressions and statementsreturn
yield
return
There 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 return
the 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 yield
expressions, 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,
f
if the function is an ordinary function,generator
it will be executed when assigning a value to a variable. However, the functionf
is a Generator function, sonext
the function will only be executed when the method is calledf
.
1.4 next() method parameters
yield
The expression itself does not return a value, or it always returns undefined
. next
The method can take one parameter, which will be treated as yield
the 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 expressionnext
is 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.yield
reset
undefined
next
true
reset
true
i
-1
-1
This function has very important grammatical significance. When the Generator function resumes operation from the paused state, its context remains unchanged. Through next
the 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 next
without 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 NaN
of 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 .value
NaN
Next
z
undefined
value
5 + NaN + undefined
NaN
next
b
next
x+1
6
next
yield
12
y
24
y / 3
8
next
yield
13
z
13
x
5
y
24
return
42
Note that since
next
the method's parameters represent the return value of the previous expression, passing parameters is invalidyield
the first time the method is used . The V8 engine directly ignores the parameters when the methodnext
is 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.next
next
next