一、Genter函数:
function命令和函数名之间有一个星号(ES6没有规定星号写在哪个位置 只要在function和函数名之间即可)
函数体内部使用yield语句定义不同的内部状态
调用genterator函数后返回一个指针对象,是一个遍历器对象,每次调用该遍历器对象的next方法,内部指针就上一次执行的地方开始,到当前yield语句为止。
- yield语句是暂停标志,不能写在普通函数中,否则会报错,即使是在generator里的普通函数
- yield如果在一个表达式中,必须放在圆括号里
- next方法可以带参数,表示上一条yield语句的返回值
二、Genterator的for…of循环
一旦next方法的返回对象的done属性为true,for…of循环就会众智,且不包含该返回对象,所以return的内容不会获取到。
三、Genterator.prototype.throw()
每个Genternator对象有一个throw方法,可以在函数体外抛出错误,在Genterator函数体内捕获。
var g = function* () {
while (true) {
try {
yield;
} catch (e) {
if (e != 'a') throw e;
console.log('内部捕获', e);
}
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
}
// 打印内容:
// 内部捕获 a
// 外部捕获 b
上面代码中,遍历器对象i连续抛出两个错误。第一个错误被Genterator函数体内的catch语句捕获,然后Genterator函数执行完成,第二个错误被函数体外的catch语句捕获。
- 如果不是使用遍历器对象的throw方法抛出,而是使用throw命令的话,只能在函数体外的catch语句中捕获。
- 如果Generator函数内部部署了try…catch代码块,遍历器throw方法抛出的错误不会影响下一次遍历,否则下一次遍历会终止
- Genterator函数定义的内部出现错误也可以被函数外部捕获,出现错误之后,遍历器不会继续执行下去,此后还调用next方法,将返回一个value属性等于undefined,done属性等于true的对象。
四、Genterator.prototype.return()
Genterator函数返回的遍历器对象有return方法,可以返回给定的值,并结束Genterator函数的遍历,如果return方法调用时不提供参数,则返回值的value为undefined。
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next(); // { value: 1, done: false}
g.return("foo"); // { value: "foo", done: true}
g.next(); // { value: undefined, done: true}
五、yield*语句
在Genterator函数内部调用另一个Genterator函数,默认情况下没有效果。
如 foo函数、bar函数,在bar函数中调用foo函数不能将bar函数里面的元素添加到foo函数中。
但是可以通过yield* foo()完成调用 如果不添加也即 yield foo()的话,调用的时候返回一个遍历器对象,任何数据只要有Iterator接口,就可以使用yield遍历。
六、Genterator函数的this
Genterator函数中是不能直接通过This对象增加属性的。
function* g() {
this.a = 11;
}
let obj = g();
obj.a; // undefined
如果在this前面增加yield,使用new命令就无法生成g的实例了,g返回的是内部的一个指针。
function * g() {
yield this.x = 2;
yield this.y = 3;
}
new g(); // 创建不了g的实例但是该对象是一个Iterator对象,具有next方法。
如果想将Generator函数当做正常的构造函数使用,可以使用以下变通的方法。
function * g() {
yield this.x = 2;
yield this.y = 3;
}
var obj = {};
var f = g.bind(obj)();
obj // { x:2, y:3 }
七、应用
1、异步操作的同步化表达
Genterator函数具有暂停执行效果,可以把异步操作写在yield语句中,当该异步操作完成之后,调用Generator的next方法。
function * main() {
var result = yield request("http://some.url"); //将异步操作写在yield语句中
var resp = JSON.parse(result);
console.log(resp.value);
}
// 整个main函数是用同步的方式表示的,但是yield控制了在调用第一次yield的时候只执行了第一句代码,在makeAjax中调用异步操作成功之后,通过next再调用接下来的代码。
function request(url) {
makeAjax(url, function(response) {
it.next(response); //异步操作完成调用接下来的语句
});
}
var it = main();
it.next(); //调用异步操作
- 注意在上面代码中请求成功之后再调用next一定要传入返回的值,因为yield表达式本身没有返回值。(只执行yield后面的语句,下次从结束的地方开始,不设置next值,result为undefined
- 如果下面的操作依赖于多个异步请求,可以使用数组的形式。(控制流管理)
function * parallelDownloads() {
let [text1, text2] = yield [
taskA,
taskB
];
console.log(text1, text2);
}
2、部署Iterator接口
可以利用Genterator接口在任何对象上部署Iterator接口。
可以获取到Object的所有key(Object.keys),再遍历对象,将key和value添加到yield中。
然后实例化该Genterator函数传入对象即可。
3、作为数据解构
Genterator可以看做数据解构,可以为任意表达式提供类似数组的接口(yield里的值可以是任意类型)