generator生成器

      generator 生成器是 ES6 提供主要用来解决异步编程深度嵌套的问题。generator 和其他函数不同的是:一般的函数只有一次返回值,generator 可以返回多次值。我们可以把它理解为一个函数内部状态的遍历器,我们每执行一次,函数的内部的状态就会发生一次改变。

定义及调用

定义:

1
2
3
4
5
function* gen() {
yield "Lucy";
yield "DaMing";
return "OK"
}

      generator 在形态上与普通函数有两点区别:一是,function 和函数名之间有一个”*“号;二是,函数体内部使用 yield(产出)语句,定义函数内部不同的状态。yield 关键字用来暂停和继续一个生成器函数,我们可以在需要的时候控制函数的运行。yield 语句的特点是必须等上一条 yield 语句执行完毕才会执行下一条 yield 语句。这样的好处是可以帮助我们用同步的方式写异步代码。避免陷入回调地狱。

调用:

1
2
3
4
5
console.log(gen()); //gen {<suspended>}
console.log(g1.next()); //{value: "Lucy", done: false}
console.log(g1.next()); //{value: "DaMing", done: false}
console.log(g1.next()); //{value: "OK", done: true}
console.log(g1.next()); //{value: undefined, done: true}

      首先,我们看到控制台将 gen()直接打印出来,是一个 Iterator 实例,然后我们再去执行 Iterator 实例的 next()方法,那么这个函数才开始真正运行,并把 yield 后面的值包装成固定对象的并返回,遇到 return 或者函数结束没有更多的值返回时,再返回 undefined。也就是函数在执行过程中的时候,如果没有遇到 return 语句或者没有 return 时,控制权无法交回被调用的代码。

返回多个值的函数

      返回多个值有什么用?斐波那契数列就是一个很好的例子。接下来,我们分别用普通函数和 generator 生成器看下是怎么实现一个产生斐波那契数列的函数。
      斐波那契数列:前两个值相加等于第三个值,再前面两个值相加等于后面的值(比如:0,1,1,2,3,5)

普通函数:

1
2
3
4
5
6
7
8
9
function fb(max) {
let a = 0, b = 1, arr = [0, 1];
while (arr.length < max) {
[a, b] = [b, a + b];
arr.push(b);
}
return arr;
}
console.log(fb(6)); //[0, 1, 1, 2, 3, 5]

generator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function* fb(max) {
let a = 0, b = 1, n = 0;
while (n < max) {
yield a;
[a, b] = [b, a + b];
n++;
}
return;
}
var f = fb(5);
console.log(f.next()); //{value: 0, done: false}
console.log(f.next()); //{value: 1, done: false}
console.log(f.next()); //{value: 1, done: false}
console.log(f.next()); //{value: 2, done: false}
console.log(f.next()); //{value: 3, done: false}
console.log(f.next()); //{value: undefined, done: false}

      我们可以看到以上都是采用手动调用的方式,非常的麻烦,接下来我们看下怎么能够实现自动遍历。

自动遍历 generator

for..of

      Iterate 是一个迭代器,只要是迭代器就可以循环输出,for…of 循环可以自动遍历 Generator 函数生成的 Iterator 对象。但是,return 的东西不会遍历,主要遍历 yield 的东西。

1
2
3
4
5
6
7
8
9
10
function* gen() {
yield "Lucy";
yield "DaMing";
return "OK"
}
let g1 = gen();
//for..of遍历
for (var item of gen(3)) {
console.log(item); //Lucy,DaMing
}

解构赋值

      解构解的是 yield 的值

1
2
3
4
5
6
7
8
9
10
11
function* gen() {
yield "Lucy";
yield "DaMing";
return "OK"
}
let g1 = gen();
//解构赋值
const [a, b, c] = gen();
console.log(a, b, c); //Lucy DaMing undefined
const [x, ...y] = gen();
console.log(x, y); //Lucy ["DaMing"]

扩展运算符

      还可以配合扩展运算符去使用

1
2
3
4
5
6
7
8
function* gen() {
yield "Lucy";
yield "DaMing";
return "OK"
}
let g1 = gen();
//扩展运算符
console.log(...gen()); //Lucy DaMing

结合 axios 数据请求

      generator 在实际应用中往往需要配合 promise 去使用,接下来我们写个实例,看看它是怎么用的。这里会用到 axios,用法这边就不详细说了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//引入axios
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
function* gener() {
let username = yield "GWJacqueline";
//获取个人信息
yield axios.get(`https://api.github.com/users/${username}`);
}
let genera = gener();
//先将username包装成固定对象的并返回
console.log(genera.next().value); //GWJacqueline
//获取信息
var backname = genera.next().value.then((res) => {
console.log(res.data);
});
console.log(backname); //{login: "undefined", id: 11791361, node_id: "MDQ6VXNlcjExNzkxMzYx", avatar_url: "https://avatars3.githubusercontent.com/u/11791361?v=4", gravatar_id: "", …}

以上的例子主要的通过 generator 去获取 github 上面有关于我的一些信息。

实例:同学录

      最后,我分别使用 generator 和迭代器实现了一个”同学录浏览功能“,可以点击同学录查看具体效果。
迭代器主要代码:

1
2
3
4
5
6
7
8
9
10
function showMessage(msg) {
let nextIndex = 0;
return {
next() {
return nextIndex < msg.length ?
{ value: msg[nextIndex++], done: false } :
{ value: undefined, done: true }
}
}
}

generator 主要代码:

1
2
3
4
5
6
7
8
function* showMessage() {
yield data[0];
yield data[1];
yield data[2];
yield data[3];
yield data[4];
} }
}

对比完两种方式,明显 generator 比迭代器实现的更加优雅,完整代码可以上我的github查看;

Copyright ©2019 guowj All Rights Reserved.

访客数 : | 访问量 :