方法
Generator对象与一种函数密切相关 Generator函数。我们在执行Generator函数,该函数不会立刻执行,而是会返回一个Generator对象。我们通过操作Generator对象来控制Generator函数的运行
Generator函数的函数体中,通过yield关键字将函数的执行过程分成了好几个阶段。
当我们调用Generator对象的next方法时,Generator函数会向下执行,直至遇到关键字yield就停止,并且返回关键字yield后面的表达式的值。
/**
* Generator函数定义
*/
function* gen() {
yield 1;
yield 2;
yield 3;
}
// 执行Generator函数,返回一个Generator对象
var g = gen();
// [object GeneratorFunction] 查看gen类型
console.log(Object.prototype.toString.call(gen));
// [object Generator] 查看g类型
console.log(Object.prototype.toString.call(g));
// 1
console.log(g.next().value);
// 2
console.log(g.next().value);
// 3
console.log(g.next().value);
// undefined
console.log(g.next().value);
我们可以为Generator对象在执行next方法执行时传入参数,作为函数下一个阶段运行的输入
// 定义Generator函数
function *gen(){
yield 1;
let i = yield 2;
yield i;
}
// 执行函数,该函数返回Generator对象
var g = gen();
// 1
console.log(g.next().value);
// 2
console.log(g.next().value);
// 12
console.log(g.next(12).value);
而当生成器函数显示return时,该函数立即变为完成状态。且return返回的值是此次调用iterator.next()返回对象的属性名为value的值。
// 定义Generator函数
function *gen(){
yield 1;
yield 2;
return 14;
yield 3;
}
// 执行函数,该函数返回Generator对象
var g = gen();
// 1
console.log(g.next().value);
// 2
console.log(g.next().value);
// 14
console.log(g.next().value);
函数执行权转移
我们可以通过Generator函数来实现函数执行时执行函数主体的转移
// 定义Generator函数 gen1
function *gen1(){
yield "gen1 1";
yield "gen1 2";
yield "gen1 3";
}
// 定义Generator函数 gen2
function *gen2(){
yield "gen2 1";
yield* gen1();
yield "gen2 3";
}
// 执行gen2函数返回一个Generator对象
var g = gen2();
// gen2 1
console.log(g.next().value);
// gen1 1
console.log(g.next().value);
// gen1 2
console.log(g.next().value);
// gen1 3
console.log(g.next().value);
// gen2 3
console.log(g.next().value);
// undefined
console.log(g.next().value);
将嵌套数组转化为扁平一维数组,来自MDN
function* iterArr(arr){
if(Array.isArray(arr)){
for(let i=0;i<arr.length;i++){
yield* iterArr(arr[i]);
}
}else{
yield arr;
}
}
var arr = ['a', ['b', 'c'], ['d', 'e']];
var gen = iterArr(arr);
arr = [...gen];
遍历二叉树,来自阮一峰老师的es6
// 下面是二叉树的构造函数,
// 三个参数分别是左树、当前节点和右树
function Tree(left, label, right) {
this.left = left;
this.right = right;
this.label = label;
}
// 下面是中序(inorder)遍历函数。
// 由于返回的是一个遍历器,所以要用generator函数。
// 函数体内采用递归算法,所以左树和右树要用yield*遍历
function* inorder(t) {
if (t) {
yield* inorder(t.left);
yield t.label;
yield* inorder(t.right);
}
}
// 下面生成二叉树
function make(array) {
// 判断是否为叶节点
if (array.length == 1) return new Tree(null, array[0], null);
return new Tree(make(array[0]), array[1], make(array[2]));
}
let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);
var result = [];
for (let node of inorder(tree)) {
result.push(node);
}
console.log(result);
使用Generator构建构造函数,来控制实例对象的实例化过程
// Generator函数
function *gen(){
yield this.a = 1;
yield this.b = 2;
yield this.c = 3;
}
// 构造函数F
function F(){
// 在当前对象挂载一个属性next,用于控制gen函数的运行
this.next = gen.call(this);
}
// 使用构造函数F
var f = new F();
// F {next: gen}
console.log(f);
// 运行gen函数
f.next.next();
// F {next: gen, a: 1}
console.log(f);
// 运行gen函数
f.next.next();
// F {next: gen, a: 1, b: 2}
console.log(f);
// 运行gen函数
f.next.next();
// F {next: gen, a: 1, b: 2, c: 3}
console.log(f);
使用Generator来实现异步编程
我们可以使用Generator函数来控制异步操作的工作流程
// 异步操作函数f1
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("f1")
resolve("第一阶段异步工作完成")
}, 3000);
});
}
// 异步操作函数f2
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("f2")
resolve("第二阶段异步工作完成")
}, 3000);
});
}
// 异步操作函数f3
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("f3")
resolve("第三阶段异步工作完成")
}, 3000);
});
}
// 操控一系列异步操作流程的Generator函数
function *gen(){
yield f1();
yield f2();
yield f3();
}
// 执行Generator函数,返回Generator对象
var g = gen();
// 开始异步第一阶段操作
g.next().value.then((res)=>{
console.log(res);
// 异步第一阶段操作完成,开始异步第二阶段操作
return g.next().value
}).then((res)=>{
console.log(res);
// 异步第二阶段操作完成,开始异步第三阶段操作
return g.next().value;
}).then((res)=>{
console.log(res);
}).catch((err)=>{
console.log(err);
})
// 同步执行
console.log("hello world");
// Generator函数
function* gen() {
yield 1;
yield 2;
yield 3;
}
// 执行Generator函数,返回Generator对象
var g = gen();
// [object Generator]
console.log(Object.prototype.toString.call(g));
// [object GeneratorFunction]
console.log(Object.prototype.toString.call(gen));
// true
/**
* 这个结果说明g的__proto__属性指向函数gen的原型
* 可以与构造函数与由构造函数创建的实例对象进行比较
*/
console.log(g.__proto__ == gen.prototype);
// 构造函数A
function A(){
this.a = 1;
this.b = 2;
}
// 实例对象a
var a = new A();
// true
console.log(a.__proto__ == A.prototype);
上面的输出结果结果说明,generator实例对象是指向generator函数的原型,而generator函数的原型就是对象Generator