声明Generator函数
Generator函数,又叫做生成器函数
//声明一个demo的Generator函数
function* demo(value){
yield `this is ${value}`;
yield `这是一个demo`;
}
看起来和普通函数没什么两样,但是他们有两个重要的区别:
- 普通函数用
function
来声明,Generator函数用function*
声明 - Generator函数内部有新的关键字:
yield
,普通函数没有
只是简单的看这个函数可能好多人都会有疑问:这个Generator函数执行后会发生什么??这个关键字yield语句有啥用??
–>那我们来试试吧
调用Generator函数
//声明一个demo的Generator函数
function* demo(val){
yield `this is ${val}`;
yield `这是一个demo`;
}
//调用demo函数
let demo = demo('小例子');
// [object Generator]
demo.next();
//{value: "this is 小例子", done: false}
demo.next();
//{value: "这是一个demo", done: false}
demo.next();
//{value: undefined, done: true}
demo.next();
//{value: undefined, done: true}
调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。
简单来说
Generator 函数
是分段执行
的,yield
表达式是暂停执行
的标记,而next
方法可以恢复执行
。
一开始,我们调用demo(‘小例子’),函数执行后返回一个 [object Generator]生成器对象
,我们把它赋值到变量demo中,仅此而已。
然后第一次调用生成器对象demo的next()方法,返回一个对象,done属性的值false,表示遍历还没有结束。
第二次…、
第三次…同样也是返回一个对象,如果没有return语句,则value属性的值为undefined,但是done属性的值true,表示遍历已经结束.
第四次调用,此时 Generator 函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值。
细心的同学会发现这里生成器的next()方法和遍历器iterator的next()方法返回的结果是不是一样。
本本拿出来记笔记了
调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。
注意:ES6 没有规定,function关键字与函数名之间的星号,写在哪个位置。所以这几种写法都可以,但是推荐使用第三种
function * fn(v){...}
function *fn(v){...}
function* fn(v){...} //推荐使用这种写法
function*fn(v){...}
yield 表达式
由于Generator函数返回的遍历器对象,只有调用
next()
方法才会遍历下一个内部状态,yield
表达式就是暂停标志。
遍历器对象的next运行逻辑:
- 遇到
yield
表达式,就暂停执行后面的操作,并将紧跟在yield
后面的表达式的值,作为返回的对象的value
属性值。 - 下一次调用
next
方法时,再继续往下执行,直到遇到下一个yield
表达式。 - 如果没有遇到新的
yield
表达式,就一直运行到函数结束,直到遇到return
语句为止,将return
语句后面的表达式的值,作为返回的对象的value
的属性值。 - 如果该函数没有
return
语句,则返回的对象的value
值为undefined
。
next()
yield
表达式本身没有返回值,或者说总是返回undefined
。next
方法可以带一个参数
,该参数
就会被当作上一个yield
表达式的返回值
。
//声明一个demo的Generator的函数
function* demo(){
let res = yield `这是一个demo`;
yield res;
}
let iterator = demo();
iterator.next(); // {value: "这是一个demo", done: false}
iterator.next('111'); // {value: "111", done: false}
函数体内的第一个yield
关键字,把它的返回值赋值给了一个变量res
。
第一次调用next(),返回对象 {value: “这是一个demo”, done: false},暂停执行
第二次调用next(),传入参数,这时第二个yield
关键字紧跟着的是变量res,而变量res的值正是上一个关键字yield
的返回值,也就是说这个值正是传入的参数。
next()的参数会作为上一个yield
的返回值
next()、throw()、return()
都知道
next()
是让Generator 函数恢复执行的,但throw()
和return()
同样也可以让Generator 函数恢复执行
简单了解一下
next()
是将yield
表达式替换成一个值
。throw()
是将yield
表达式替换成一个throw语句
。return()
是将yield
表达式替换成一个return语句
。
yield*表达式
在一个Generator函数里面,如果我们想调用另一个Generator函数,就需要用到的
yield*
话不多说直接上代码
//声明Generator函数: gen1
function* gen1(){
yield "a1";
yield "a2";
}
//声明Generator函数: gen2
function* gen2(){
yield "b1";
yield "b2";
}
//声明Generator函数: gen
function* gen(){
yield "start";
yield* gen1();
yield* gen2();
yield "end";
}
let ite = gen();
ite.next(); // {value:"start",done:false}
ite.next(); // {value:"a1",done:false}
ite.next(); // {value:"a2",done:false}
ite.next(); // {value:"b1",done:false}
ite.next(); // {value:"b2",done:false}
ite.next(); // {value:"end",done:false}
看完例子我们会知道:
这里用了关键字yield*
来实现调用另外两个Generator函数
如果一个Generator函数gen执行过程中,进入(调用)了另一个Generator函数gen1/gen2,那么会一直等到Generator函数gen1/gen2全部执行完毕后,才会返回Generator函数gen继续执行。
应用场景
Generator
可以暂停函数执行
,可以将异步操作的语句写到yield后面,通过执行next方法进行回调。
- 异步操作的同步化表达
Generator
函数的暂停执行的效果,意味着可以把异步操作写在yield
表达式里面,等到调用next
方法时再往后执行。
这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield
表达式下面,反正要等到调用next
方法时再执行。
所以,Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数
。 - 控制流管理
回调地狱的解决方案之一
- 部署 Iterator 接口
利用 Generator 函数,可以在任意对象上部署 Iterator 接口
- 作为数据结构
Generator 可以看作是数据结构,更确切地说,可以看作是一个数组结构,因为 Generator 函数可以返回一系列的值,这意味着它可以对任意表达式,提供类似数组的接口。