“This is the second day of my participation in the August More Text Challenge.

preface

Promise, as one of the solutions to asynchronous programming, was actually based on callback functions and didn’t change the syntactic structure of asynchronous writing. Generator functions further optimize the solution as asynchronous programming, allowing Generator functions to pause during execution and then resume execution from the pause point. Execution of a function is typically relinquished during an asynchronous operation and resumed at the same location after completion. So the Generator syntax can handle asynchronous tasks in a synchronous syntax.

The iterator

Before we look at generator functions, it’s important to look at iterators. To eliminate the error-prone complexity of the classic for loop, which requires setting up multiple index variables to track,ES6 introduced iterators, a special object with a next() method designed specifically for the iterative process. Each call to next() returns an object containing the value and done attributes. In addition, iterators provide a consistent iterator protocol compliant interface to unify iterable traversal. For example, for… The of statement can be used to iterate over an iterable containing an iterator (such as Array, Map, Set, etc.).

The generator

Generator is a function that returns an iterator. Generator is like a function, denoted by function*, and contains the yield keyword. Unlike a function, a generator can return multiple times with yield in addition to a return statement. Calling a generator function does not immediately execute the code inside the generator, but rather returns an iterator object for the generator. Because the Generator can return multiple times during execution, it looks like a function that can remember the execution state.

function* hello() {
   var a = 'b'
   yield 'a';
   return a;
}

var gen = hello();
console.log(gen);
// => hello {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: undefined}
console.log(gen.next())
// => {value: "a", done: false}
console.log(gen.next())
// => {value: "b", done: true}
Copy the code

The above code generates a generator by calling hello(), and the internal code is not executed. Next returns an object with value yield, and done indicates whether the iterator is complete. When the iterator is complete, done will be true, value will be the value of return, and next will continue, value will be undefined

The yield keyword

The yield keyword can be used to pause and resume a generator function. The value of the expression following yield is returned to the caller of the generator, and yield can be considered based on the return keyword of the generator version. The yield keyword can be followed by any value or expression. Once a yield expression is encountered, the generator’s code is paused until the generator’s next() method is called. Each time a generator’s next() method is called, the generator continues execution at the statement immediately following yield. Stop until the next yield or exception is thrown internally by the generator or the end of the generator function or return statement is reached. Note that the yield keyword can only be used within a generator; using it elsewhere will result in a syntax error. This is true even when used within a generator internal function.

Next method

Generator.prototype.next() returns an object containing the properties done and value, and can also take a parameter to pass to the Generator. The returned value object contains the same done and value meanings as in the iterator section, and there is nothing more to say. Of interest, the next() method can take an argument that replaces the return value of the previous yield statement inside the generator. Undefined if no yield statement is passed. One exception is that whatever arguments are passed to the next() method are discarded the first time it is called. Because the argument passed to the next() method replaces the return value of the previous yield, and no yield statement is executed until the first call to the next() method, passing the argument on the first call is meaningless. In fact, the ability to pass values inside an iterator is very important. For example, in an asynchronous process, the generator function suspends at the yield keyword, and after the asynchronous operation is complete, the current asynchronous value must be passed for use by subsequent iterator processes.

gen = gen(); // Convert to generator object
var g = gen.next(); // Start the first step
var firstStep = g.value; // get the expression after the first yield, i.e. 1 + 2 = 3
g = gen.next(firstStep); // If we pass in the value of the first step from the next function, ret is 3
var secondStep = g.value; // Get the second yield value, which is 4
g = gen.next(secondStep); // Pass in the value of the second step from the next function, that is, num is 4
{value: 7, done: false}} {value: 7, done: false}
g = gen.next(g.value);
console.log(g.done); // true
Copy the code

conclusion

Generator functions serve as a further optimization solution for asynchronous programming, allowing asynchronous logic to be written out synchronously for easier understanding.