什么是Generator?
Generator(生成器)属于ES6规范,可以将Generator看作一个可以暂停和恢复执行的函数
示例:
function* a(){
let b=yield 'b'
console.log(b);
yield 'b';
return 'c'
}
Generator通过 function* 进行定义,yield关键字定义了Generator内部的不同状态。
Generator函数调用时不是立即返回结果,而是生成一个generator对象,可以通过对象来控制Generator的执行过程,例如
function* a(){
let b=yield 'a'
console.log(b);
yield 'b';
return 'c'
}
let b=a();
console.log(b.next());
console.log(b.next('foo'));
console.log(b.next());
Generator函数被执行时返回一个暂停的Generator对象,可以用这个对象空值执行的过程;
每次调用该对象的next方法可以恢复Generator执行,执行过程遇到yield关键字会暂停执行;
如果Genertor对象的next方法有掺水,yield会结构并返回该参数(并赋值给yield左侧变量);
因此,在Generator外部可以通过传入的next参数来随时调整Generator后续行为;
function* c(){
let d=yield 'e';
console.log(d);
if(d=== 'foo'){
console.log('if');
}
else{
console.log("else");
}
}
let f=c();
console.log(f.next());
console.log(f.next('foo'));
Iterator接口的实现和应用
实际上Generator对象同时实现了Iterator接口与可迭代协议,Generator实现了Iterator中的,next,return,throw方法,同时和其他类似数组对象一样具有Symbol.iterator属性
因此,我们可以通过for of,扩展运算符来自动执行它,而不需要手动.next()来执行
另外,可以通过yield* 实现非线性迭代,可以在yield*后面加上另一个Generator方法或者其他任意可迭代对象
function* all(){
yield 'a1';
yield 'a2';
}
function* bll(){
yield 'b1';
yield* all();
yield 'b2';
yield* [1,23,2];
}
for(let g of bll()){
console.log(g);
}
输出:b1,a1,a2,b2,1,23,2
协程
Generator实现了协程的思想,即控制权的主动让出和恢复
yield关键字能够主动暂停执行,并将执行权转交还给Generator的调用者,调用者调用next(),generator再从之前的位置恢复执行,这么做的优点有:
- 程序自身可以ikongzhi各个子程序的切换
- 让异步操作看上去像同步操作
常见的生产者和消费者模式:生产者(producer)生产数据,消费者(consumer)处理数据
function* produceGen(){
while(true){
let data=produceData(); //生产数据
yield data;
}
}
function* consumer(){
let producer = produceGen();
while(true){
data = producer.next().value
consume() //处理数据
}
}
consumer();
由于Generator函数不能自动执行,要实现文件顺序读取,还需要通过不断调用next方法分次执行Generator函数中的代码;
日常工作可以直接借助co库来接收一个Generator函数,自动执行完内部的逻辑