一、 Generator的定义
1.特点
- Promise 是为了解决回调地狱(Callback Hell)的问题而出现的,而生成器函数(Generator Functions)则是为了解决异步问题而出现的。
- 普通函数在调用时会立即执行,并一直执行到函数体中的所有代码执行完毕或遇到 return 语句返回结果。这种执行方式称为"一次性执行"。
- Generator函数在调用时不会立即执行函数体中的所有代码,而是返回一个迭代器(Iterator)。通过迭代器可以控制生成器函数的执行,可以暂停执行、恢复执行和产生多个值。
2.基本用法
//定义生成器函数
function* gen(num) {
let r1 = yield 1
console.log('r1', r1);
let r2 = yield 2
console.log('r2', r2);
let r3 = yield 3
console.log('r3', r3);
return 4;
}
// 定义迭代器对象
const iterator = gen()
/*
遇到 yield 1 停止执行
输出结果:
{value:1, done:false}
value 表示yield后面的值或return的值
done 表示生成器是否已完成
还没有走到函数的return语句或最后一次.next时,done总是false
*/
console.log(iterator.next())
/*
输出结果:
r1 undefined
{value:2, done:false}
yield的返回值并不会返回给等号左边的变量, 因为generator 函数在遇到 yield 时就已经暂停执行了,并不会执行到赋值操作
如果想赋值可以用 next 方法进行传值
iterator.next('A')
*/
console.log(iterator.next())
/*
输出结果:
r2 A
{value:3, done:false}
next先进行赋值操作,将参数赋值给r2
*/
console.log(iterator.next('A'))
/*
输出结果:
r3 undefined
{value:4, done:true}
如果函数有return值,最后一个next方法,它的value值为return的值;如果没有值为 undefined; done为true。
*/
console.log(iterator.next())
3.Generator函数嵌套使用
function* gen1() {
yield 1
yield 2
}
function* gen2() {
yield 3
// generator函数的嵌套
// 这种写法对应 方案1
// yield gen1()
yield* gen1()
yield 4
}
const iterator = gen2()
console.log(iterator.next()); // {value:3,done:false}
// 如果我们想执行到 gen1 中的 yield 值
// console.log(iterator.next()); // {value:generator实例,done:false}
// let itor = iterator.next().value
// console.log(itor.next()); // {value:1,done:false}
// console.log(itor.next()); // {value:2,done:false}
// 方案2
console.log(iterator.next()); // {value:1,done:false} 你需要在yield后面加一个*,让它知道后面是一个generator对象
console.log(iterator.next()); // {value:2,done:false}
console.log(iterator.next()); // {value:4,done:false}
console.log(iterator.next()); // {value:undefined,done:true}
二、引入co库
- Generator 函数本身并没有提供自动执行的能力,需要通过手动调用
next()方法来控制函数的执行。 - co 是一个第三方库,它是基于 Generator 函数的自动执行器。co 库可以自动迭代 Generator 函数的迭代器,并根据
yield表达式返回的 Promise 对象的状态,自动调用next()方法,并将 Promise 的解析值传递给 Generator 函数。它简化了使用 Generator 函数进行异步编程的过程。
const co = require('co');
function* myGenerator() {
try {
const result1 = yield Promise.resolve('Result 1');
console.log(result1);
const result2 = yield Promise.resolve('Result 2');
console.log(result2);
} catch (error) {
console.error('Error:', error);
}
}
co(myGenerator())
.then(() => {
console.log('Generator function completed');
})
.catch(error => {
console.error('Error:', error);
});
/*
Result 1
Result 2
Generator function completed
*/
三、async/await原理(手写 Generator自动执行器)
async/await 是基于 Generator 函数和 Promise 的组合使用。下面是使用 Generator 函数来手动实现一个类似于 async/await 的功能。
function runGenerator(generator) {
const iterator = generator(); // 获取 Generator 函数的迭代器
function iterate({ value, done }) {
if (done) {
return Promise.resolve(value); // 如果迭代器已完成,直接返回结果
}
return Promise.resolve(value) // 将当前 yield 表达式的值转为 Promise 对象
.then((result) => {
return iterate(iterator.next(result)); // 递归调用 iterate 函数,继续执行下一个 yield 表达式
})
.catch((error) => {
return iterate(iterator.throw(error)); // 如果出现异常,使用迭代器的 throw 方法抛出异常
});
}
return iterate(iterator.next()); // 开始执行第一个 yield 表达式
}
四、生成器的迭代(手写for...of遍历obj对象)
1.生成器的迭代
- 生成器对象可以通过
for...of循环进行迭代,或者使用扩展操作符...将生成器转换为数组。
function* myGenerator() {
yield 'a';
yield 'b';
yield 'c';
}
for (const item of myGenerator()) {
console.log(item); // 'a', 'b', 'c'
}
const arr = [...myGenerator()];
console.log(arr); // ['a', 'b', 'c']
2.手动实现使用 for...of 循环遍历对象
const person = {
name: 'lzh',
age: 21
}
// 方法一
person[Symbol.iterator] = function* () {
yield* Object.values(this)
}
// 方法二
person[Symbol.iterator] = function* () {
for (let x in this) {
yield this[x]
}
}
//方法三
person[Symbol.iterator] = function () {
const keys = Object.keys(this);
let index = 0;
return {
next:()=> {
if (index < keys.length) {
return {
value: this[keys[index++]],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}