概念:
- generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次
- ES6提供的解决异步编程的方案之一
- Generator函数是一个状态机, 内部封装了不同状态的数据
- 用来生成遍历器对象
- 可暂停函数(惰性求值), yield可暂停, next方法可启动。 每次返回的是yield后的表达式结果
特点:
- function 与函数名之间有一个星号
- 内部用yield表达式来定义不同的状态
generator基本语法:
{
// genertaor基本定义
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()方法会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。如果done为true,则value就是return的返回值。
当执行到done为true时,这个generator对象就已经全部执行完毕,不要再继续调用next()了。
方法:
Generator.prototype.next()
返回一个由 yield表达式生成的值。
Generator.prototype.return()
返回给定的值并结束生成器。
Generator.prototype.throw()
向生成器抛出一个错误。
generator和函数的对比:
我们先复习函数的概念。一个函数是一段完整的代码,调用一个函数就是传入参数,然后返回结果:
function foo(x) {
return x + x;
}
var r = foo(1); // 调用foo函数
函数在执行过程中,如果没有遇到return语句(函数末尾如果没有return,就是隐含的return undefined;),控制权无法交回被调用的代码。
generator跟函数很像,定义如下:
function* foo(x) {
yield x + 1;
yield x + 2;
return x + 3;
}
generator和函数不同的是,generator由function定义(注意多出的号),并且,除了return语句,还可以用yield返回多次。
示例:
一个无限迭代器
function* idMaker(){
let index = 0;
while(true) //无限迭代
yield index++;
}
let gen = idMaker(); // "Generator { }"
console.log(gen.next().value);
// 0
console.log(gen.next().value);
// 1
console.log(gen.next().value);
// 2
// ...
generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。这个好处要等到后面学了AJAX以后才能体会到。
没有generator之前的黑暗时代,用AJAX时需要这么写代码:
ajax('http://url-1', data1, function (err, result) {
if (err) {
return handle(err);
}
ajax('http://url-2', data2, function (err, result) {
if (err) {
return handle(err);
}
ajax('http://url-3', data3, function (err, result) {
if (err) {
return handle(err);
}
return success(result);
});
});
});
回调越多,代码越难看。
有了generator的美好时代,用AJAX时可以这么写:
try {
r1 = yield ajax('http://url-1', data1);
r2 = yield ajax('http://url-2', data2);
r3 = yield ajax('http://url-3', data3);
success(r3);
}
catch (err) {
handle(err);
}
看上去是同步的代码,实际执行是异步的。
状态机事例
{
//状态机事例
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}
}
抽奖实例
//HTML部分代码省略
{
//抽奖实例
let draw = function(count){
//具体抽奖逻辑
console.log(`剩余${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)
}
长轮询实例
{
//长轮询
let ajax = function* (){
yield new Promise((resolve,reject) => {
//模拟异步过程
setTimeout(function () {
resolve({code:0});
},200);
})
}
let pull = function(){
let genertaor = ajax();
let step = genertaor.next(); //step是一个Promise对象
step.value.then(function(d){
if(d.code != 0){
setTimeout(function(){
console.info('wait');
pull();
},1000);
}else{
console.info(d)
}
})
}
pull();
}
参考链接:
Generator - JavaScript | MDN
generator - 廖雪峰的官方网站
es6(十) genertaor - zh__quan的博客 - CSDN博客