(十) Generator函数 (生成器)
1. Generator函数的基本概念
Generator
函数是es6提供的一种异步编程的解决方案
目前为止学过的异步处理的手段:
- 定时任务
- promise
有如下特性:
- 使用function声明, 在function关键字和函数名之间有一个
*
号 - 函数内部使用
yield
表达式 - 调用Generator函数并不会执行, 而是会返回一个迭代器对象
利用返回的迭代器对象, 调用内部的next方法, 返回一个包含value和done的结果对象
function* generator() {
yield 'hello'
yield 'world'
return 100
}
let gen = generator()
// console.log(gen[Symbol.iterator]() === gen) true
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
2. yield 表达式
yield表达式只能写在生成器函数里面
yield
表达式相当于一个暂停标识, 当迭代器对象调用next
方法后
- 遇到yield表达式, 将紧跟在yield后面的表达式的值作为value的值, 然后, 会在此处暂停, 等待下一个next的调用
- 再调用next, 往下执行后面的代码, 如果又碰到yield, 重复上一步
- 如果往后都没有yield, 就将return后表达式的值,作为最后的value的值, done为true
- 如果最后也没有return, 最后value的值为undefined
关于有return 和 没有 return 注意观察 done的状态
function* generator() {
yield 'hello'
yield 'world'
// return 100
}
let gen = generator()
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
function* generator() {
yield 'hello'
yield 'world'
return 100
}
let gen = generator()
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
当然, 也可以在生成器函数中不使用yield,此时生成器函数就变成了单纯的暂缓执行函数
2.1 返回值
yield
表达式没有返回值, 也可以说返回值是undefined
但是可以通过调用next
方法时, 为next
方法传递参数,该参数会作为上一个yield表达式的返回值
function* generator() {
let flag = yield
if (flag) { return 100 }
return 200
}
let gen = generator()
console.log(gen.next()) // {value: undefined, done: false}
console.log(gen.next()) // {value: 200, done: true}
上下对比
function* generator() {
let flag = yield
if (flag) { return 100 }
return 200
}
let gen = generator()
console.log(gen.next()) // {value: undefined, done: false}
console.log(gen.next(true)) // {value: 100, done: true}
因此, 可以利用这一特性, 在Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为
2.2 for...of循环
使用for...of 循环遍历生成器函数返回的迭代器对象, 会自动调用next方法
到此处感觉跟python中的yield比较像了
斐波那契数列
function* fibonacci() {
let [a, b] = [0, 1]
for (let i = 0; i < 10; i++) {
// 注意看这里, 加了分号结尾的
yield b;
[a, b] = [b, a + b]
}
}
for (let num of fibonacci()) {
console.log(num);
}
// 正常输出结果
如果不加分号
function* fibonacci() {
let [a, b] = [0, 1]
for (let i = 0; i < 10; i++) {
// 注意看这里, 加了分号结尾的
yield b[a, b] = [b, a + b]
}
}
for (let num of fibonacci()) {
console.log(num);
}
// 输出一连串的 [1, 1] 疑惑
yield表达式后面记得加分号
3. 迭代器对象调用return
返回的迭代器对象有一个return()
方法, 与在生成器函数内部使用return一致
在哪个next方法后面使用, 就相当于在哪个yield后面使用
可以接收一个参数, 作为value的值, 不传参value为undefined
内部有try..finally, 当使用return后, 会立即进入finally , 与一般使用一致
function* generaror() {
// try {
// yield 1
// yield 2
// } finally {
// yield 3
// yield 4
// }
yield 1
yield 2
yield 3
yield 4
}
let gen = generaror()
console.log(gen.next());
console.log(gen.return(100));
4. yield* 表达式
yield 表达式可以让我们在一个生成器函数中调用另外一个生成器函数*
function* fn1() {
yield 1
yield 2
}
function* fn2() {
yield 3
yield 4
yield* fn1()
}
for (let i of fn2()) {
console.log(i); // 3 4 1 2
}
5. this问题
6. 异步请求
本质是通过next传参
function request(url) {
axios.get(url).then(res => {
gen.next(res)
})
}
function* generator() {
// 获取name
let res1 = yield request('./data1.json')
console.log(res1);
// 通过name获取age
let res2 = yield request('./data2.json')
console.log(res2);
}
let gen = generator()
gen.next()
仅记录自己的学习总结,如有错误,还请评论指正~