前面的文章中为大家讲解了Promise、async/await、Event Loop 等关于JS异步的文章,其中,关于async/await,我们知道,它是基于Generator的一个语法糖,而其返回的又是一个Promise,对于Promise我们已经不再陌生,并且它有着很实际的开发应用。那么关于Generator,今天小编就来带着大家简单了解一下。本人觉得Generator知识点和API繁多,对于一般的开发者来说,只需要掌握其中几个重要的概念和API就可以。本文主要讲解yield和next,这也是Generator中最重要的两个概念,对于以后对其他基于Generator封装的轮子的学习和Generator的其他知识扩展有更大帮助和必要。
什么是Generator?
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
首先,我们看一下Generator函数的语法,看一下究竟是哪里与传统函数完全不同。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
我们根据上面的描述创建一个Generator函数
function* helloGenerator(){
yield 'hello'
console.log('我是一个普通表达式')
yield 'Generator'
return '执行完毕'
}
Generator函数的调用
我们按照规则定义了一个Generator函数,现在我们调用它,Generator函数的调用方式和普通函数相同
var hg = helloGenerator()
console.log(hg,'helloGenerator调用结果')
我们在控制台打印我们调用的结果如下
捕获.PNG
我们发现,Generator函数并不能像普通函数那样直接返回结果,如果是普通函数,我们在调用赋值的时候,就可以拿到函数调用的结果,这就Generator函数的不同。
按照官方文档的介绍,我们应该这样调用它
hg.next()
//为了方便看执行结果,我们打印一下
console.log(hg.next())
我们看一下执行的结果
我们看到了通过
next
调用后Generator函数的返回值,它是一个对象,其中value
是我们yield
后面的值,另外还有一个done
属性,它是什么呢?我们发现通过next
调用后的Generator函数并没有一下执行完,而是返回了第一个yield
后面的结果,下面我们接上面代码再用next
调用一次
console.log(hg.next())//上一次调用
console.log(hg.next())
我们看下执行结果
我们看到,我们第二次通过
next
调用,函数接着上一次往下执行,并且有一次停在了yield
的地方。那么我们可以猜想,yield就像是让函数暂停了,而next则让函数结束暂停状态接着向下执行,直到下一个‘断点’。为了验证我们的猜想,我们接着使用
next
来调用。
console.log(hg.next())
console.log(hg.next())
console.log(hg.next())
console.log(hg.next())
console.log(hg.next())
console.log(hg.next())
我们用多个next
来调用,下面我们看一下结果
我们发现,结果是符合我们上面的猜想的,并且,当执行到return
的时候,done
属性的值就变成了true
。并且 以后所有的执行结果中value
属性都是undefined
,done
属性都是true
。
于是我们得到了下面的结论
Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。
文章到这里,介绍了Generator最基本且必要的两个核心概念。到这里我们知道了,我们可用Generator来把函数分段执行。也可以利用其和普通函数调用方法不一样的特点,写一些特殊的扩展功能,下面是一个暂缓执行的函数的例子
function* f() {
console.log('执行了!')
}
var generator = f();
setTimeout(function () {
generator.next()
}, 2000);
上面的函数中,我们并没有使用yield
,如果是普通函数,我们在调用赋值阶段就已经执行了。而我们用到generator函数就会等next
来调用它。
关于yield
和next
的使用注意事项,下面简单罗列几项
-
yield
表达式后面的表达式,只有当调用next
方法、内部指针指向该语句时才会执行
function* gen() {
yield 123 + 456;
}
上面代码中,yield
后面的表达式123 + 456
,不会立即求值,只会在next
方法将指针移到这一句时,才会求值。
-
yield
表达式与return
语句既有相似之处,也有区别。相似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield
,函数暂停执行,下一次再从该位置继续向后执行,而return
语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return
语句,但是可以执行多次(或者说多个)yield
表达式。正常函数只能返回一个值,因为只能执行一次return;Generator 函数可以返回一系列的值,因为可以有任意多个yield。如果函数中没有yield
只有return
,那么函数就会一直执行到return
。 -
yield
表达式只能用在 Generator 函数里面,用在其他地方都会报错。(自行验证)
文章到这里就结束了,本文章只是带大家验证和了解Generator最基本的只是,需要深入了解的同学,还需要好好专研。
本文参考阮一峰《ECMAScript 6 入门》
谢谢您的阅读
网友评论