基本概念
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。
Generator函数有多种理解角度。从语法上,首先可以把它理解成,Generator函数一个状态机,封装了多个内部状态。
执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。
形式上,Generator函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号,二是,函数体内部使用yield语句,定义不同的内部状态。(yield语句在英语里的意思就是“产出”)
next函数的用法
let tell = function* (){
yield 'a';
yield 'b';
return 'c'
};
let k = tell();
console.log(k.next());//{value: "a", done: false}
console.log(k.next());//{value: "b", done: false}
console.log(k.next());//{value: "c", done: true}
console.log(k.next());//{value: undefined, done: true}
调用第一个next,会去执行第一个yield,调用第二个next,会去执行第二个yeild,
以此类推,函数体内部是个异步调用的过程
yield*的语法
使用genertor也可以作为遍历器的返回值
let obj = {};
obj[Symbol.iterator] = function* (){
yield 1;
yield 2;
yield 3;
};
for(let value of obj){
console.log(value);//1 2 3
}
高级用法,状态机
generator特别适用的情况
let state = function* (){
while(1){
yield 'A';
yield 'B';
yield 'C';
}
}
let status = state();
console.log(status.next());//{value: "A", done: false}
console.log(status.next());//{value: "B", done: false}
console.log(status.next());//{value: "C", done: false}
console.log(status.next());//{value: "A", done: false}
console.log(status.next());//{value: "B", done: false}
console.log(status.next());//{value: "C", done: false}
console.log(status.next());//{value: "A", done: false}
以上的例子中,无论你连续调用多少次.next(),done的值都不可能是true,会一直循环打印。
还有一种async形式的,但是浏览器需要添加兼容包才可识别,所有的功能和方法的使用都和上面的一样,结果也一样。反正先写下来,做一个提示:
let state = async function (){
while(1){
await 'A';
await 'B';
await 'C';
}
}
let status = state();
console.log(status.next());//{value: "A", done: false}
console.log(status.next());//{value: "B", done: false}
console.log(status.next());//{value: "C", done: false}
console.log(status.next());//{value: "A", done: false}
console.log(status.next());//{value: "B", done: false}
console.log(status.next());//{value: "C", done: false}
console.log(status.next());//{value: "A", done: false}
使用实例1
有一个例子,看看实际如何运用。
以下例子中,实现前端抽奖剩余次数限制,使用generator做判断
let draw = function (count) {
//具体业务逻辑
console.info(`剩余${count}次`)
};
//计算剩余次数,原来使用的是全局变量,现在通过generator来进行判断
let residue = function* (count){
while (count>0){
count--;
yield draw(count);
}
};
let star = residue(5);
let btn = document.createElement('button');
btn.id = 'start';
btn.textContent = '抽奖';
document.body.appendChild(btn);
document.getElementById('start').addEventListener('click', function (){
star.next();
},false);
使用实例2
长轮循:服务端某一个数据状态定期的变化,前端需要获取时,除了webSocket,还常轮循,由于webSocket的浏览器兼容不好,所以长轮循还是很常用的,之前的做法是使用定时器定时的发送请求,现在通过generator来实现。
let ajax = function* (){
yield new Promise(function(resolve, reject){
//这里应该写请求,现在使用定时器代替
setTimeout(function (){
resolve({code:0});
},200);
});
};
let pull = function (){
let generator = ajax();
let step = generator.next();
step.value.then(function(d){
if(d.code != 0){
setTimeout(function (){
console.log('wait');
pull();
},1000);
}else{
console.log(d);//{code: 0}
}
});
};
pull();