一、前言
第一次看koajs的示例时,发现该语句 function *(next){...............} ,这是啥啊?于是搜索一下,原来这是就是ES6的新特性Generator Function(生成器函数)。
那什么是生成器函数呢?其实就相当于C#2.0中通过yield关键字实现的迭代器的生成器(细节有所不同),那么理解的关键就在迭代器和yield关键字两部分了。下面将尝试从表象出发,逐步对生成器函数及利用它进行异步编程进行浅层的分析理解。
二、表象——语法及基本使用
示例:


1. 生成器语函数定义
普通函数添加*号后则成为了成为了生成器函数了。
生成器函数的行为与普通函数并不相同,表现为如下3点:
1. 通过new运算符或函数调用的形式调用生成器函数,均会返回一个生成器实例;
2. 通过new运算符或函数调用的形式调用生成器函数,均不会马上执行函数体的代码;
3. 必须调用生成器实例的next方法才会执行生成器函数体的代码。


2、 关键字yield——迭代器生成器
用于马上退出代码块并保留现场,当执行迭代器的next函数时,则能从退出点恢复现场并继续执行下去。下面有2点需要注意:
1. yield后面的表达式将作为迭代器next函数的返回值;
2. 迭代器next函数的入参将作为yield的返回值(有点像运算符)。
3、迭代器(Generator)
迭代器是一个拥有 {value:{*}, done:{Boolean}} next([*])方法 和 {undefined} throw([*])方法 的对象,通过next函数不断执行以关键字yield分割的代码段,通过throw函数令yield分割的代码段抛出异常。
三、核心1——迭代器
迭代器更多的是指迭代器模式,迭代器模式是指通过一个名为迭代器的对象按一定的规则遍历集合元素,调用者只需告诉迭代器获取下一个元素即可,而集合的类型、如何获取元素等因素均由具体的迭代器自行处理。(又一次地关注点分离!)并且由于迭代器模式可以做到 按需执行/延迟执行 的效果,因此能降低遍历无限序列时内存/栈溢出的问题,也能作为异步编程模式使用。
模式理解的注意点:
1. 迭代器每次进访问集合的一个元素,并由调用者发起访问请求时迭代器才执行下一次访问操作
2. “按一定的规则”,意味着不一定遍历集合中所有的元素,并且规则可以内聚到迭代器的具体实现上,也可通过策略模式外移到其他模块中;
3. “集合”,集合可以是一开始就已经初始化好的有限序列集合(如[1,2,3,4,5,6,7]),也可以是按需生成的无限序列集合(如1到无限大)
4. “集合元素”,可以是整数集合、字符串集合等数据集合,也可以是函数等指令+数据的集合;
若触过C#、Java等服务端语句的朋友应该对迭代器有一定程度的了解,C#的IEnumrable、IEnumerator和Java的Iterable、Iterator就是跟迭代器相关的接口定义,继承上述接口的迭代器实现均可以通过foreach或for...in语句作循环操作。
那么这里有2点是要注意的:
1. 迭代器是指设计模式,跟具体的语言无关,因此所有语言均可根据该模式实现具体的迭代器;
2. foreach或for...in语句是语法层面的支持,跟迭代器模式没有必然联系。(若语法层面不支持,那函数式编程中的递归的效果是一样的,假如编译器/解析器支持尾递归则更好了,可以JS不支持)
下面我们通过迭代器来实现Python中的range函数,并通过range函数创建一个超大的有限序列正整数集合(直接用数组的话绝有可能导致栈溢出哦!)。


由于JS是单线程运行,并且当UI线程被阻塞N秒后,浏览器会询问是否停止脚本的执行,但上述代码并不会由于序列过大造成栈溢出的问题。假如预先生成1到99999999999999999999或更大数字的数组,那很有可能造成stack overflow。那是由于迭代器实质为一状态机,而调用next函数则是触发状态的转换,而状态机中同一时刻用于存放变量的存储空间固定,并不会出现无限增长的情况。
四、核心2——yield关键字
回到关键字yield上了,其实yield关键字就是以一种更直观、便捷的方式让我们创建用于遍历有限序列集合的迭代器,而yield则用于将生成器函数的代码切片作为有限序列集合的元素(元素的类型为指令+数据,而不仅仅是数据而已)。下面我们一起看看yield关键字是怎样对代码切片的吧!


上述代码最终会被解析为下面的代码:


五、异步调用中的应用
由于迭代器模式实现 延迟执行/按需执行,因此可作为一种异步编程模式来应用。


主逻辑中异步调用的写法与同步调用的基本没差异了,爽了吧!但异步任务模型与生成器函数及其生成的迭代器耦合性太大,还是不太好用。下面我们通过实现了Promises/A+规范的Q来进一步解耦。
若执行引擎不支持关键字yield,那么上述代码不就无法执行了吗?还是那句话,yield关键字其实就是语法糖,最终还是会被解析为一个迭代器。因此我们自行实现一个迭代器也是能实现上述效果的,不过过程会繁琐很多(若如第2节的示例那样存在try...catch语句,就繁琐死了@~@),并且代码的整洁性、可维护性就全靠攻城狮来保证了。(语法糖从语法层面简化编程和维护难度,但理解底层的工作原理也十分重要哦!)
六、与Q结合


暂未阅读Q的源代码,暂不作详细分析。反正API就这样用,呵呵!
七、与iPromise结合
iPromise是我开发的一个Promises/A+的完整实现,阅读源码你会发现它继承了jQuery.Deferred1.5~2.1、jsDeferred、mmDeferred和Promises/A官网实现示例的精妙设计,并且从v0.0.6开始支持ES6特性GeneratorFunction。使用示例如下:




八、总结
Generator Function并不是为异步编程而生,但可以将它结合Promise来实现良好的异步编程模型。本篇内容仅简单介绍Generator Function及相关的异步编程内容,若有纰漏请各位指正,谢谢!
九、 参考
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
如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!
<a href="http://home.cnblogs.com/u/fsjohnhuang/">^_^肥仔John</a>
<a href="http://home.cnblogs.com/u/fsjohnhuang/followees">关注 - 85</a>
<a href="http://home.cnblogs.com/u/fsjohnhuang/followers">粉丝 - 707</a>
<a>+加关注</a>
2
<a></a>
评论列表
我还需要慢慢体会,还不是太明白。
http://pic.cnblogs.com/face/343299/20150320135757.png
刚修改了一下,也许会描述得更清楚些!
http://pic.cnblogs.com/face/347002/20141205140116.png
<a href="http://www.ucancode.com/index.htm" target="_blank">【推荐】超50万VC++源码: 大型工控、组态\仿真、建模CAD源码2018!</a>
<a href="https://cloud.tencent.com/developer/support-plan?fromSource=gwzcw.710852.710852.710852" target="_blank">【推荐】加入腾讯云自媒体扶持计划,免费领取域名&服务器</a>
<b>最新IT新闻</b>:
<b>最新知识库文章</b>:
<a href="https://github.com/fsjohnhuang" target="_blank">肥仔John@github</a>
作品:
<a href="https://github.com/fsjohnhuang/iScheme" target="_blank">iScheme—Scheme解释器</a>
<a href="https://github.com/fsjohnhuang/preview" target="_blank">preview.js—纯前端的图片预览组件</a>
<a href="https://github.com/fsjohnhuang/iPromise" target="_blank">iPromise.js—Promises/</a>