ES6 中规定, Generator 函数内部使用 this 无效,而且不能使用 new 关键字来获取 Generator 的一个实例。
1、内部使用 this
如下方法可以使得 Generator 内部的 this 关键字有效:
function* a() {
this.a = 1;
yield "hello";
yield "world"
};
let b = a();
console.log(b.a, b.next(), b.next());
//undefined { value: 'hello', done: false } { value: 'world', done: false }
let c = a.call(a.prototype);
console.log( c.next(), c.next(), c.a)
//{ value: 'hello', done: false } { value: 'world', done: false } 1
这样,执行两次 next() 将 Generator 里面的数据执行完后,将 this 绑定到 a 的原型上面去。若还没有使用 next() 处理完 Generator 里面的数据,this 是不会绑定到 a.prototype 上去的。
2、状态机
Generator 是实现状态机的最佳结构,比如如下代码:
var clock = function* () {
while (true) {
console.log('Tick!');
yield;
console.log('Tock!');
yield;
}
};
let a = clock();
a.next(); //Tick!
a.next(); //Tock!
a.next(); //Tick!
a.next(); //Tock!
a.next(); //Tick!
let b = clock();
b.next(); //Tick!
b.next(); //Tock!
a.next(); //Tock!
3、控制流管理
利用for…of循环会自动依次执行yield命令的特性,提供一种更一般的控制流管理的方法。
ES6 之前,进行流程控制,我们大多数使用的是回调函数:
function step1(val, fun) {
console.log("step" + val++);
if (typeof fun == "function") fun(val);
return val;
}
function step2(val, fun) {
console.log("step" + val++);
if (typeof fun == "function") fun(val);
return val;
}
function step3(val, fun) {
console.log("step" + val++);
if (typeof fun == "function") fun(val);
return val;
}
function step4(val, fun) {
console.log("step" + val++);
if (typeof fun == "function") fun(val);
return val;
}
//原本流程控制需要使用回调函数的方式来实现,如下:
function task(init) {
step1(init, function (val1) {
step2(val1, function (val2) {
step3(val2, function (val3) {
step4(val3, function (val4) {
//......
})
})
})
});
}
task(1);
//采用 Promise 改写上面的代码:
Promise.resolve(step1(1))
.then(val => step2(val))
.then(val => step3(val))
.then(val => step4(val));
//Generator 函数实现上面代码逻辑
function* rangeTask(initVal) {
try {
let val1 = yield step1(initVal);
let val2 = yield step2(val1);
let val3 = yield step3(val2);
let val4 = yield step4(val3);
//...
} catch (e) {
console.log(e)
}
}
function scheduler(task) {
let obj = task.next(task.value);
if (!obj.done) {
task.value = obj.value;
scheduler(task);
}
}
scheduler(rangeTask(1));
注意:
上面这种做法,只适合同步操作,即所有的task都必须是同步的,不能有异步操作。因为这里的代码一得到返回值,就继续往下执行,没有判断异步操作何时完成。