2020.12.9 16:44
01.generator(生成器)
- generator(生成器)是一个函数
- generator 函数是 ES6 提供的一种异步编程解决方案
- generator 函数有多种理解角度。从语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态
02.generator函数和普通函数的区别
书写格式
- 普通函数:
function generator(参数){代码...}
- 生成器函数:
function* generator(参数){代码...}
注意*
- 函数体内部使用
yield
语句,定义不同的内部状态(yield
在英语里的意思就是“产出”) - ES6没有规定,function与函数名之间的星号,写在哪个位置。这导致下面的写法都能通过。
function * generator(a,b){ ··· yield ··· }
function *generator(a,b){ ··· yield ··· }
function* generator(a,b){ ··· yield ··· }
function*generator(a,b){ ··· yield ··· }
与普通函数的对比:
//普通函数
function generator(a,b){
alert(a);
alert(b);
}
generator(5,10);//普通函数调用后会连续弹出a和b
//generator函数
function *generator(a,b){
alert(a);
yield;
alert(b);
}
let genObj=generator(5,10);//函数名()不能直接调用 实际上返回了一个对象
console.log(genObj instanceof Object);//true
genObj.next();//调用一次只会弹出a,因为遇到yield停止了
genObj.next();//再调用一次才会弹出b
执行过程中
- 普通函数:普通函数在执行过程中,如果没有遇到
return
语句(函数末尾如果没有return
,就是隐含的return undefined
),控制权无法交回被调用的代码(执行过程中不能停止) - 白话理解:普通函数在执行过程中不能停止
- 生成器函数:生成器函数在执行过程中除了
return
语句,还可以用yield
返回多次 - 白话理解:生成器函数在执行过程中可以停止(等待某件事情完成),再执行(剩余部分)
调用
- 调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对
- 下一步,必须调用遍历器对象的next方法
Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行
03.yield
- yield可以穿参数也可以返回
1.传参:
function *show(){
alert("a");
let a=yield;
alert("b");
alert(a);//5
}
let gen=show();
gen.next(12);//"a"
gen.next(5);//"b",5
//会依次弹出"a","b",5
简单分析:
第一次调用next,只执行如下代码
function *show(){
alert("a");
yield;//因为碰到yield所以停止执行
第二次调用next,执行如下代码
let a;//第二次调用gen.next(5);传递的参数5实际上就赋给了a
alert("b");
alert(a);//所以弹出a的值是5
}
给yeild之前的部分传参(和普通函数穿参一样)
function *show(参数1,参数2){ 参数1和参数2在这里使用 yield ··· }
let gen=show(参数1,参数2);
如果函数前漏掉 *
- 就是普通函数
- 如果有yield会报错, ReferenceError: yield is not defined
- yield 只能在Generator函数内部使用
总结:第一次调用next无法给yield传参,第二次调用才能成功传参,给yield之前的部分传参和给普通函数传参相同
2.返回:
function* show(){
alert('a');
yield 12;
alert('b');
//如果最后一步想要返回值只能return
//return 55;
}
let gen=show();
let res1=gen.next();
console.log(res1);//{value:12,done:false} done,完成的,值为false表示函数未执行完
let res2=gen.next();
//不写return 55输出{value:undefined,done:true} 值为true表示函数已执行完
//写return 55输出{value:55,done:true}
console.log(res2)
通过做菜步骤来类比yield:
步骤:买来的菜==>清洗==>洗好的菜==>切==>切好的菜==>炒==>炒好的菜
function* Vegetables(v){//传入买来的菜v
let x=`洗好的${v}`;//1.在这里清洗(中间步骤),操作后x就是洗好的菜(中间结果)
//2.遇到yield,停止执行,返回x==>洗好的菜,这里是第一步,整个过程未完成,对应{value:'洗好的菜',done:false}
let y=yield x;//3.let y=x 这时y的值是洗好的菜
y=='洗好的菜'?y='切好的菜':y='';//4.在这里切菜(中间步骤),操作后y就是切好的菜(中间结果)
//5.遇到yield,停止执行,返回y==>切好的菜,这里是第二步,整个过程未完成,对应{value:'切好的菜',done:false}
let z=yield y;//6.let z=y 这时z的值是切好的菜
z=='切好的菜'?z='炒熟的菜':z='';//7.在这里炒菜(最终步骤),操作后z就是炒好的菜(最终结果)
return z; //8.return返回z,这里是第三步,整个过程已完成,对应{value:'炒熟的菜',done:true}
}
let veg=Vegetables('菜');
console.log(veg);
let res1=veg.next();//第一次调用会执行1和2==>传入参数,操作后返回第一步操作产生的中间结果
console.log(res1);//{value:'洗好的菜',done:false}
let res2=veg.next(res1.value);//第二次调用会执行3,4,5==>传入第一步产生的中间结果,操作后返回第二步操作产生中间结果
console.log(res2);//{value:'切好的菜',done:false}
let res3=veg.next(res2.value);//第三次调用会执行6,7,8==>传入第二步产生的中间结果,操作后返回第三步操作产生最终结果
console.log(res3.value);//{value:'炒熟的菜',done:true}
console.log(veg);
运行结果: