新版JS中,yield估计是最吸引人的新功能,特别是Node出来之后,大家被异步折腾的够呛,而借助于yield
可以用比较优雅的处理异步流程。 但是yield
关键字,早就出现在其他语言当中了,我知道的有python和c#。这篇从最基本的原理讲起,希望大家能更好的理解yield
。 关于yield
是什么,可以看这篇介绍
先从迭代器模式讲起。简单地讲,迭代器是一种遍历集合的方式。它的接口很简单,一般拥有以下三个方法就可以了。
hasNext() //集合中是否还有下一个元素next() //迭代到下一个元素reset()//重置,我见到的代码一般是抛出异常,即一般不支持多次迭代
那么我们来实现一个简单的迭代器吧
function Range(min,max){//[min,max) return {
cur:min,
hasNext:function(){
return this.cur<max;
},
next:function(){
return this.cur++;
},
reset:function(){
throw new Error('unsupportted operation');
}
}}for(var iter = Range(1,10);iter.hasNext();){
i = iter.next();
console.log(i);}
对于迭代的过程,一般会做一些语法糖,然后用起来就很舒服了,像下面:
for(var i : Range(1,10)){
console.log(i);}
function fib(){
return {
state :0,
cur :0,
prev1:-1,
prev2:-1,
hasNext:function(){
return true;
},
//fib数列,第一个是0,第二个是1,后面就是统一的迭代公式了 next:function(){
if(this.state==0){
this.cur = 0;
this.state=1;
}else if(this.state == 1){
this.cur =1;
this.prev2=0;
this.state=2;
}else{
this.prev1 = this.prev2;
this.prev2 =this.cur;
this.cur = this.prev1+this.prev2;
}
return this.cur;
}
//ignore reset funciton }}//这是无限序列,所以改造了一下,只生成8个数var fibIter = fib();for(var i = 0;i<8;i++){
console.log(fibIter.next());
if(fibIter.hasNext())
continue;}
这个例子可以展示迭代器模式更多的特点:
迭代器模式是很常用的设计模式,但是实现起来,很多东西是程序化的;当迭代规则比较复杂时,维护迭代器内的状态,是比较麻烦的。 于是有了generator,何为generator,这里 说的很明确: Generators: a better way to build Iterators.
就是实现迭代器的更好的方式,借助 yield
关键字,可以更优雅的实现上面的fib数列。下面的代码需要环境支持 JS 1.7,如何让你的环境支持1.7,请参见http://html-js.com/article/1687
function* fib2(){
yield 0;//状态0,第一次调用next,返回0,并改变状态 yield 1;//状态1,第二次调用next,返回1,并改变状态
var p1 = 0
,p2 =1
,cur = p1+p2;
while(true){
yield cur;//状态2,后面调用next,返回相应的几个,状态不在改变
p1 = p2;
p2 = cur;
cur = p1+p2;
}}var fibIter2 = fib2();for(var i =0;i<8;i++){
console.log(fibIter2.next().value);}
对比上面自己实现的迭代器,应该可以看出,yield
是解释器给我们提供的语法糖,但是对比上下的代码,用yield
,更简洁,而且逻辑也更好懂。
**上面对于状态的描述,是我自己写的,编译器(解释器)不一定会生成一样的东西。这些原理,可以从反编译的C#代码看出来:yield的确会转化成相应的状态机。JS的实现可能不一样,但是对于理解yield的行为,是没有影响的
yield
与异步那yield
,怎么解决异步的问题呢。 通过上面的分析,yield
之后,实际上本次调用就结束了,控制权实际上已经转到了外部调用了generator的next方法的函数,调用的过程中伴随着状态的改变。那么如果外部函数不继续调用next方法,那么yield
所在函数就相当于停在yield
那里了。所以把异步的东西做完,要函数继续执行,只要在合适的地方再次调用generator 的next就行,就好像函数在暂停后,继续执行。
本文分享自 交互设计前端开发与后端程序设计 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!