In the Promise section I mentioned async/await and generator. I promised you an update, and today it’s here.
The Generator function
We must talk about generator before we can talk about async/await, because async/await is the syntactic sugar of generator. As everyone at the front end knows, The Generator has the following features:
- There is an asterisk “*” between the function keyword and the function name.
- Function bodies use yield expressions to define different internal states (there can be multiple yields).
- Direct call
Generator
The function does not execute and does not return the result of the run, but returns oneTraverser object(Iterator Object). - To call the traverser object in turn
next
Method, traversalGenerator
Every state inside the function.
I don’t think that’s enough, but let’s take a look at what’s in this function after we declare it:
- First of all, we have
prototype
Under the__proto__
Found a method to mount under the instancenext()
.return()
.throw()
. These methods are definitely something we have to figure out. Keep looking down. - Ordinary functional
__proto__
The followingconstructor
Are allFunction()
, indicating that is byFunction
Instantiate out of the object, but its isGeneratorFunction()
. That’s a problem.
So far these two problems have been identified by this picture. Let’s tackle the first one first, but before that I want to make a brief introduction as a basis. In normal functions, we want the final result of a function, and we usually return it, or we end the function with a return. A function cannot be interrupted while it is running, nor can values be passed in from outside the function. However, this is broken in Generator, so it is completely different from other functions. When a Generator is declared as function*, it is possible to have a number of states inside, with yield separating breakpoints. We call the generated Generator, which returns an iterator object and uses methods on the object to achieve a yield output.
function* generator() {
yield 1
yield 2
};
let iterator = generator();
iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: undefined, done: true}
Copy the code
Methods on Generator instances
next()
Next () is called to iterate over the traverser object, and each time it is called, it returns an object:
value
Value is the internalyield
The return value of an expression.- There is also a value indicating whether execution is complete
done
.next()
I can’t find the backyield
Keyword, the return value istrue
“And because we didn’t find the last oneyield
Expression, sovalue
forundefined
. - If the
yield
It was executed beforereturn
, so go over toreturn
Is returned{value: undefined, done: true}
No matter how it is iterated later, it will not perform the heat afteryield
.
2) We can also pass arguments to next(), which can be used for the next output.
function* gen(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var g = gen(5);
g.next() // { value:6, done:false }
g.next(15) // { value:10, done:false }
g.next(20) // { value:55, done:true }
Copy the code
- Primary operation
next
Method, the system automatically ignores the parameter and returnsx+1
The value of 6; - Second call
next
Method will be last timeyield
The expression is set to 15, so y equals 30, and returnsy / 3
The value of 10; - Third call
next
Method will be last timeyield
The expression is set to 20, so z is equal to 20, x is equal to 5, y is equal to 30, so the return statement is equal to 55.
You can see that this parameter is treated as the return value of the previous yield statement. As a result of… Of is a looping method for iterating over an iterable, so it also applies to the traverser object returned by our generator. There is no need to call next. Similarly, methods of the same nature can iterate over the value of the traverser object, such as the extension operator array.from ().
function* gen(x) {
yield 1;
yield 2;
yield 3;
}
var g = gen();
for (let item of g) {
console.log(item) / / 1, 2, 3
}
[...g] / / [1, 2, 3]
Array.from(g) / / [1, 2, 3]
Copy the code
return()
Whenever return() is called, the done value of the returned result is immediately set to true, terminating the traversal of the generator function, regardless of the number of yield pairs in the generator. If the return() function does not transmit parameters, value is undipay, and after the parameters are transferred, value is the transferred parameter.
function* generator() {
yield 1
yield 2
yield 3
yield 4
}
let g = generator()
console.log(g.next()); // {value: 1, done: false}
console.log(g.return()); // {value: undefined, done: true}
console.log(g.next(5)); // {value: undefined, done: true}
console.log(g.return(new Date())) // {value: Mon Sep 13 2021 23:37:57 GMT+0800, done: true}
console.log(g.next()); // {value: undefined, done: true}
Copy the code
throw()
After the throw() function is called, the error can be caught by a try/catch inside the Generator. The done value of the returned result is immediately set to true, ending the traversal of the Generator function. If the throw() function does not pass parameters, the captured exceptions are undipay, and after the parameters are transferred, the captured exceptions are the transferred parameters. Error() is usually instantiated in conjunction with this
function* gen() {
try {
yield 1;
yield 2;
yield 3;
yield 4;
} catch (e) {
console.log("Generator internal error", e); }}var g = gen();
console.log(g.next()); // {value: 1, done: false}
console.log(g.next()); // {value: 2, done: false}
try {
g.throw(new Error("Something went wrong")); // Generator internal Error: Something went wrong
g.throw(new Error("oh no"));
} catch (error) {
console.log('External error', error); // External Error: oh no
}
console.log(g.next()); // {value: undefined, done: true}
Copy the code
The traverser object throws two errors, the first of which is caught internally by the Generator. Second, since the catch inside the function body has already been executed and no longer catches the error, the Generator body is thrown and caught by the catch outside the function body. Executing the next() method after that does not traverse further down.
How is the Generator instantiated internally
In fact, whether we declare an array, string, method, is actually generated by instantiating the corresponding constructor.
But that’s not a generalization, there are differences, and we’ll talk a little bit about what happens when you define base data types, the difference between new Function and Function declaration, and how base and reference data types are stored in memory. How are we instantiating the function here to generate the generator? If GeneratorFunction is printed on the console, there is no corresponding constructor, indicating that it is not on the window. So use the method of obtaining the prototype object, to find the constructor on his prototype object.
console.dir(Object.getPrototypeOf(function* (){}).constructor)
/ / print
1. GeneratorFunction()
1. 1. arguments: [Exception: TypeError: 'caller'.'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:2:14)]
1. caller: [Exception: TypeError: 'caller'.'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:2:14)]
1. length: 1
1. name: "GeneratorFunction"
1. prototype: GeneratorFunction
1. 1. arguments: (...).1. caller: (...)
1. constructor: ƒ GeneratorFunction()
1. prototype: Generator {constructor: GeneratorFunction.next: ƒ.return: ƒ.throw: ƒ.Symbol(Symbol.toStringTag): "Generator1 "}.Symbol(Symbol.toStringTag): "GeneratorFunction"1.__proto__: ƒ ()
1. __proto__: ƒ Function()
1. [[Scopes]] :Scopes[0] // Let's verify that the constructor we found is correctgeneratorConstructor offunction* gen() {
yield 1
}
console.log(gen instanceof Object.getPrototypeOf(function* (){}).constructor) / / true yes
Copy the code
If object.getPrototypeof (function*(){}).constructor is found as a constructor for the generator, then in turn we should be able to generate the generator by instantiating the constructor.
var GeneratorFunction = Object.getPrototypeOf(function* () { }).constructor;
var g = new GeneratorFunction("num"."yield num * 2");
console.log(g); ƒ* anonymous(num) {yield num * 2}
var iterator = g(10);
console.log(iterator.next().value); / / 20
console.log(iterator);
/*__proto__: Generator * [[GeneratorLocation]] * [[GeneratorStatus]]: "suspended" * [[GeneratorFunction]]: ƒ* anonymous(num) * [[GeneratorReceiver]]: Window * [[Scopes]]: Scopes[2] */GeneratorStatus has two values"suspended"and"closed"Corresponding donetrueandfalse
Copy the code
As it turns out, you can generate a Generator function object by instantiating GeneratorFunction.
Add: yield* expression
If we want to invoke another generator function within a generator function, we can use yield* to do so.
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(i) {
yield i;
yield* anotherGenerator(i); // Transfer of executive authority
yield i + 10;
}
var gen = generator(10);
console.log(gen.next().value); / / 10
console.log(gen.next().value); / / 11
console.log(gen.next().value); / / 12
console.log(gen.next().value); / / 13
console.log(gen.next().value); / / 20
Copy the code
The yield* expression indicates that yield returns an traverser object to be used inside a Generator function.
The Generator function concludes with a summary:
- Define using
function*
As a function declaration. - call
Generator
Function, returns aIterator objectNot the result of execution, internallyGeneratorStatus
There aresuspended
.closed
Two status values, corresponding to whether completed or not. - There can be more than one inside
yield
To do something like a normal functionreturn
The function. - It can be provided through the API
next()
Method, or statement for an iteration loopfor... of
.Array.from()
, expand the operator.
forIterator objectTo output eachyield
After the values of thenext()
You can also pass parameters as the previous oneyield
The value participates in the next operation. The API andreturn()
.throw()
GeneratorFunction
是generator
Constructor, but not mounted inwindow
On.yield *
Expressions can be in aGenerator
Function returns another oneGenerator
Function of theTraverser object.
If this article can help or inspire you, it will be my pleasure