1.Generator
- 基本概念
- 异步编程的一种解决方案;
- Generator包含多个步骤,遇到每一个步骤的标志是yield或者return,遇到yield或者return,这步就停止了,不会向下运行了;进行下一步要调用next函数,直到结束;
- next函数的用法
- yield*的语法
2.generator返回的就是一个iterator接口;
通过next的方式不断去做函数体内部的几个阶段,执行tell的时候,会遇到第一个yield的时候停下来,执行完了yield之前的语句,调用next的时候会执行第一个yield;在执行next的时候,会执行到下一个yield或者return,以此类推,从而保证了这个函数体内部看上去是一个异步操作的过程;
generator基本定义:
{
let tell = function*(){
yield 'a';
yield 'b';
return 'c'
};
let k = tell();
console.log(k.next());
console.log(k.next());
console.log(k.next());
console.log(k.next());
//打印结果:
Object{value:"a",done:false}
Object{value:"b",done:false}
Object{value:"c",done:true}
Object{value:undefined,done:true}
}
3.generator函数和iterator接口的关系;
-
任意一个对象的iterator接口都是部署在Symbol.iterator属性上;
-
generator函数就是一个遍历器生成函数,所以可以直接把它赋值给
Symbol.iterator从而使这个对象也具备了iterator接口; -
generator也可以作为遍历器的返回值;
{ let obj ={}; obj[Symbol.iterator] = function*(){ yield 1; yield 2; yield 3; } for(let value of obj){ console.log('value',value); } //value 1 //value 2 //value 3 }
4.什么情况下generator有这最大的优势呢?
-
状态机
比如:需要a、b、c三种状态描述一种事物,这种事物只存在abc三种状态;a-b-c-a;- 调用next方法走到第一个yield,下一个next会到B,在next到C,在next
的时候,因为while(1)会无限循环,又回到A,B-C-A-B...;
- 调用next方法走到第一个yield,下一个next会到B,在next到C,在next
{
//状态机
let state =function*(){
//while(1)永远循环
while(1){
yield 'A';
yield 'B';
yield 'C';
}
};
let status = state();
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
//输出结果:
Object{value:"A",done:false}
Object{value:"B",done:false}
Object{value:"B",done:false}
Object{value:"A",done:false}
Object{value:"B",done:false}
}
5.async
{
let state =async function(){
while(1){
await 'A';
await 'B';
await 'C';
}
};
let status = state();
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
//输出结果:
Object{value:"A",done:false}
Object{value:"B",done:false}
Object{value:"B",done:false}
Object{value:"A",done:false}
Object{value:"B",done:false}
}
6.什么时候用generator可以发挥强大的作用
eg:抽奖次数限制;
* 当前用户还可以抽奖5次;用户点击一次,抽奖变4次...1次,抽奖剩余逻辑,前端要做限制,服务端也要做;
好处:次数没有保存到全局变量中,通过初始化直接给generator传一个参数进去,让它不断的循环和业务的状态点,从而达到一个抽奖的次数限制和状态的变化;
{
//显示:抽奖逻辑这块;剩余5次,当点了抽奖按钮的时候,这个过程
要执行抽奖的逻辑,随机过程,到哪个奖品上;还要显示当前剩余几次;
//draw这个函数实现的是集体抽奖逻辑和当前剩余次数的显示;
let draw =function(count){
//具体抽奖逻辑
//怎么计算当前还有多少次呢?
--之前的做法:设一个全局的变量来保存当前的次数,这样非常不安全的;
别人知道你的变量是什么,直接修改你的变量,都拦不住了;
尽量少把数据存放在全局对象上,影响页面性能;
console.info(`剩余${count}次`);
};
let residue =function*(count){
//判断count是不是大于0,大于0还可以抽奖
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('star').addEventListener('click',function(){
star.next();
},false);
}
7.长轮询
- 服务端的某一个数据状态定期的去变化,前端需要定时的去服务端取这个状态,因为http是无状态的连接,我们要怎么实时取得服务端的变化呢;
- 两种方式:长轮询(之前做法通过定时器不断的访问一个接口;通过generator可以把长轮询的代码写的更加优雅,把跟业务逻辑相关的区分开)、websocket(浏览器兼容不好);
{
//长轮询;模拟ajax一个过程
//这个ajax表示对接口的模拟;
let ajax = functon* (){
yield new Promise(function(resolve,reject){
//模拟请求的耗时;用定时器模拟一下;
//真实的环境中,setTimeout重写,写成真实的接口;在接口的成回调中,把
resolve写一下,就可以直接拿来用了;
setTimeout(function(){
//接口成功返回了,执行resolve;比如最后返回的状态接口code:0;
resolve({code:0});
//resolve({code:1});会不断的轮询;
},200);
});
};
//轮询的过程;执行后端通信一次;
let pull =function(){
//对generator实例化;
let generator = ajax();
//取得当前generator的步骤,next相当于让generator进行了一次运行; 这个地方会
运行第一个的yield,返回一个promise实例,这个promise实例,对后端通信服务端接口进行一次连接,
查询一次;这里通过延时 200ms来表达200ms进行一次通信,然后接下一步;
let step = generator.next();
//value是promise实例,通过then异步操作,拿到d表示拿后端通信的数据,就是{code:0};
step.value.then(function(d){
//判断如果数据拿到的是最新的;!=0就不是新的,要再一次的请求;
if(d.code!=0){
//每一秒钟请求一次;
setTimeout(function(){
//查询中
console.info('wait');
pull();
},1000);
}else{
//如果拿到最新数据,长轮询查询结束;直接把数据打出来;
console.info(d);
}
});
};
//相当于进行了一次轮询,因为{code:0}就查询了一次;
pull();//Object{code:0}
//{code:1}会每隔1200ms都是在输出wait,一直在进行一个长轮询的查询;
}
网友评论