One, foreword

See koajs sample for the first time, found that the statement function * (next) {… }, what is this? Generator Function ES6 is a new feature.

So what is a generator function? This is equivalent to the generator of the iterator implemented with the yield keyword in C#2.0 (details vary), so the key to understanding lies in the iterator and the yield keyword. The following will attempt to begin with a superficial understanding of generator functions and their use for asynchronous programming.

2. Representation — Grammar and basic usage

  Example:

Function *enumerable(MSG){console.log(MSG) var msg1 = yield MSG + 'after' console.log(msg1) var msg2 = yield msg1 + ' after' try{ var msg3 = yield msg2 + 'after' console.log('ok') } catch(e){ console.log(e) } Console. log(msg2 + 'over')} var enumerator = enumerator (' Hello ') var ret = enumerator.next() Ret {value:'hello after',done:false} ret = enumerator.next('world') {value:'world after',done:false} ret = enumerator.next('game') {value:'game after',done:false} ret = enumerator.throw(new Error('test')) Then it says Game over. The ret value is {done:true} // for... Enumerator = Enumerable ('hello') for(ret of Enumerator) console.log(json.stringify (ret)); // The console displays // hello // {value:'hello after',done:false} // world // {value:'world after',done:false} // {value:'game ' after',done:false} // game over // {done:true}Copy the code

 1. Generator language function definition

function* test(){}
function * test(){}
function *test(){}
test = function* (){} 
test = function *(){}Copy the code

A normal function with an asterisk becomes a generator function.

Object. The prototype. ToString. Call (test) / / show [Object GeneratorFunction]Copy the code

Generator functions behave differently from normal functions in the following three ways:

1. Calling a generator function either through the new operator or as a function call returns an instance of the generator;

2. Calling a generator function through the new operator or function call does not immediately execute the code in the function body;

3. You must call the next method of the generator instance to execute the generator body code.

Function *say(MSG){console.log(MSG)} var gen = say(' Hello world' The console. The log (Object. The prototype. ToString. Call (gen)) / / show [Object Generator] gen. The next () / / display hello worldCopy the code

The keyword yield — iterator generator

Used to exit the code block immediately and preserve the scene, and when executing the iterator’s next function, to resume the scene from the exit point and continue execution. Here are two things to note:

1. The yield expression is returned as the iterator next function;

2. The input of the iterator next function is returned as yield (sort of like an operator).

3. Iterators (Generator)

An iterator is an object with a {value:{*}, done:{Boolean}} next([*]) method and {undefined} throw([*]) method. Throw an exception to a yield-partitioned section of code.

Core 1 — iterator

Iterator more refers to the iterator mode. The iterator mode refers to traversing collection elements according to certain rules through an object named iterator. The caller only needs to tell the iterator to get the next element, while the type of collection and how to get elements are handled by the specific iterator. (Another separation of concerns!) And because iterator mode can execute on demand/delay, it can reduce the memory/stack overflow problem when iterating through infinite sequences, and can also be used as asynchronous programming mode.

Points to note in pattern understanding:

1. The iterator accesses one element of the collection at a time, and only performs the next access when the caller initiates the access request

2. “according to certain rules” means not necessarily traversing all elements in the set, and rules can be condensed into the concrete implementation of the iterator, or can be migrated to other modules through the policy mode;

3. “set”, a set can be a finite set of sequences initialized at the beginning (e.g. [1,2,3,4,5,6,7]) or an infinite set of sequences generated on demand (e.g. 1 to infinity)

4. “Set element”, can be integer set, string set and other data set, can also be functions and other instructions + data set;

Iterators are the interface definitions for C# and Java iterators. For example, IEnumrable of C# and IEnumerator of Java are the interface definitions for iterators. Iterator implementations that inherit the above interfaces can be implemented via foreach or for… The in statement loops.

So here are two things to note:

1. Iterators are design patterns that are language-independent, so that all languages can implement specific iterators according to this pattern;

2. The foreach or for… The in statement is syntactically supported and is not necessarily related to the iterator pattern. (Recursion in functional programming is the same if it is not supported at the syntactic level. It is better if the compiler/parser supports tail recursion, but JS does not.)

Let’s implement the range function in Python using iterators and create a large set of finite positive integers. .

Var RangeIterator = function(start,end,scan){this.start = arguments.length >= 2? start : 0 this.end = end == undefined ? start : End this. Scan = scan | | 1 this independence idx = this. Start} / / visit the iterator by the next element request / / FF and ES6 iterator interface specification defines the function of the iterator must pass called next request a visit the next element RangeIterator. Prototype. Next = function () {if (this. Independence idx > this. End) if (!!!!! StopIteration) {throw StopIteration}else{return void 0} var ret = this.idx this.idx += this.scan return ret} // Var range = function(start, end, scan){var range = new RangeIterator(start, end, scan) Scan) return {// FF command for... __iterator__: function(){return iterator}, // Exposes the iterator's next function next: Var array = [] for (var I = this.next(); var I = this.next(); var I = this.next(); i ! = void 0; I = this.next()) array.push(I) return array + ''}}} var r = range(1, 1000000000000000000000000) https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_Generators#.E5.AE.9A.E4.B9.89.E8.87.AA.E5.AE . 9 a. E4. B9.89. E8. BF. AD. The E4. BB. A3. E5.99. A8 for (var I r) in the console. The log (I)/show / 1 to 99999999999999999999 / / for all browsers (var I =  r.next(); i ! = void 0; I = r.ext ()) console.log(I) // displays 1 to 99999999999999999999Copy the code

Since JS runs in a single thread and the browser asks whether to stop executing the script after the UI thread is blocked for N seconds, the above code does not cause stack overflow due to the sequence being too large. If you pregenerate an array of numbers from 1 to 9999999999999999 or larger, you are likely to cause Stack Overflow. That’s because an iterator is essentially a state machine, and calling next triggers a state transition, and the amount of storage used to hold variables in a state machine at any one time is fixed and does not grow indefinitely.

Core 2 — yield keyword

Back to the yield keyword, which allows us to create iterators for traversing a finite set of sequences in a more intuitive and convenient way, and yield is used to slice up generator functions as elements of a finite set of sequences (elements of type instruction + data, not just data). Let’s take a look at how the yield keyword slices code.

Function *enumerable(MSG){console.log(MSG) var msg1 = yield MSG + 'after' console.log(msg1) var msg2 = yield msg1 + ' after' console.log(msg2 + ' over') }Copy the code

The above code will eventually be parsed into the following code:

var enumerable = function(msg){
  var state = -1
 
  return {
    next: function(val){
      switch(++state){
         case 0:
                  console.log(msg + ' after')
                  break
         case 1:
                  var msg1 = val
                  console.log(msg1 + ' after')
                  break
         case 2:
                  var msg2 = val
                  console.log(msg2 + ' over')
                  break
      }
    }
  }
}Copy the code

(Note: the above is only a simple analysis, more complex cases (conditional control, loop, iteration, exception capture processing, etc.) can refer to @Zhao’s “Human decompression using the keyword yield method”)

5. Application of asynchronous call

Because the iterator pattern implements deferred/on-demand execution, it can be applied as an asynchronous programming pattern.

Var iterator = getArticles('dummy.json') {iterator.next() {function getData(SRC){setTimeout(function(){  iterator.next({tpl: 'tpl.html', name: 'fsjohnhuang'}) }, 1000) } function getTpl(tpl){ setTimeout(function(){ iterator.next('hello ${name}') }, Function render(data, TPL){return tpl.replace(/\$\{(\w+)\}/, function(){ return data[arguments[1]] == void 0 ? arguments[0] : Function *getAritcles(SRC){console.log('begin') var data = yield getData(SRC) var TPL = yield getTpl(data.tpl) var res = render(data, tpl) console.log(rest) }Copy the code

Asynchronous calls are written in the same way as synchronous calls in the main logic. But the asynchronous task model is too coupled to generator functions and their generated iterators to be useful. Here we further decouple by implementing Q in Promises/A+ specifications.

If the execution engine does not support the yield keyword, then the above code would not execute. Once again, the yield keyword is just syntactic sugar and will eventually be resolved as an iterator. So we could implement an iterator ourselves, but the process would be much more tedious (if, as in the example in Section 2, we had a try… Catch statements @~@), and the cleanliness and maintainability of the code is guaranteed by the siege lion. (Syntax candy makes programming and maintenance syntactically easier, but it’s also important to understand how the underlying stuff works.)

6. Combine with Q

Resolve ({TPL: 'tpl.html', name: defer) function getData(SRC){var deferred = q.doer () setTimeout(function(){deferred. 'fsjohnhuang'}) }, 1000) return deferred.promise } function getTpl(tpl){ var deferred = Q.defer() setTimeout(function(){ Defer. Resolve ('hello ${name}')}, 3000) return deferred. Promise} // tpl){ return tpl.replace(/\$\{(\w+)\}/, function(){ return data[arguments[1]] == void 0 ? arguments[0] : Data [arguments[1]]})} // main logic q..sync (function *(){console.log('begin') var data = yield getData('dummy.json') var TPL  = yield getTpl(data.tpl) var res = render(data, tpl) console.log(rest) })Copy the code

Q source code has not been read, not detailed analysis. Anyway, this is how API is used, hehe!

Vii. Combine with iPromise

Promises/A+ iPromise is A promise /A+ implementation that I developed. If you look at the source code, you will find that it contains the following examples: jQuery.Deferred1.5~2.1, jsDeferred, mmDeferred and Promises/A. ES6 feature GeneratorFunction is supported starting with V0.0.6. The following is an example:

var getData = function(dataSrc){ return iPromise(function(r){ setTimeout(function(){ r(dataSrc + ' has loaded') }, 1000) }) } var getTpl = function(tplSrc){ return iPromise(function(r){ setTimeout(function(){ r(tplStr + ' has loaded') }, 2000) }) } var render = function(data, tpl){ throw new Error('OMG! ') } iPromise(function *(dataSrc, tplSrc){ try{ var data = yield getData(dataSrc) var tpl = yield getTpl(tplSrc) render(data, tpl) } catch(e){ console.log(e) } console.log('over! ') }, 'dummyData.json', DummyTpl. Json has loaded // dummyTpl. Json has loaded // Error: OMG! / / the Stack trace: / / test10 / render / / / / home/fsjohnhuang/repos/iPromise/test/v0.0.2 HTML: 190-6 / / display over!Copy the code

V0.6.0 is implemented recursively, as follows (github.com/fsjohnhuang…) :

// If the iterator's next function is passed the first time it is called, TypeError will be reported: To typhotyphoid generator var iterator = mixin. Apply (null, ToArray (arguments,1)) var next = function(){var deferred = iPromise() deferred. Resolve. Function (){var yieldReturn = iterator.next. Apply (iterator, arguments) return yieldReturn (function(){var yieldReturn = iterator.next. Arguments) if(yieldReturn. Done) throw Error('StopIteration') return yieldReturn. Value}). Then (next, arguments) Function (e){iterator.throw(e)})} deferred. Resolve () deferred. Then (next)Copy the code

Eight, summary

The Generator Function is not designed for asynchronous programming, but it can be combined with Promise to achieve a good asynchronous programming model. This article only briefly introduces Generator Function and related asynchronous programming content, please correct any mistakes, thank you!

Respect the original, reproduced please indicate from: www.cnblogs.com/fsjohnhuang… ^_^ fat John

Nine, reference

http://huangj.in/765

https://www.imququ.com/post/generator-function-in-es6.html

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/The_Iterator_protocol

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/function*

http://www.cnblogs.com/yangecnu/archive/2012/03/17/2402432.html

http://www.cnblogs.com/draem0507/p/3795189.html

http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript-answer-2-loop-and-interpreter.html

http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html

http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield.html