深入理解 JavaScript 生成器(Generator)(后含async/await实现原理)

什么是生成器?

生成器是由 function* 声明创建的特殊函数,它返回一个 Generator 对象,该对象符合 可迭代协议迭代器协议。生成器函数可以在执行过程中暂停,并在稍后恢复,其上下文(包括变量绑定)会在恢复时保持不变。

基本语法

function* name(param0, param1, /* …, */ paramN) {
    statements
}

特点

  1. yield 关键字:用于暂停和恢复生成器函数的执行。
  2. next() 方法:用于恢复生成器的执行,返回一个对象 { value, done }
    • value:当前 yield 表达式的值。
    • done:布尔值,表示生成器是否完成。
  3. return 语句:结束生成器的执行,并返回一个值。
  4. yield* 表达式:委托给另一个生成器或可迭代对象。

示例讲解

1. 基本用法

function* generator(i) {
    yield i;
    yield i + 10;
}

const gen = generator(10);
console.log(gen.next()); // { value: 10, done: false }
console.log(gen.next()); // { value: 20, done: false }
console.log(gen.next()); // { value: undefined, done: true }

2. for...of 遍历生成器

function* foo() {
    yield 1;
    yield 2;
    yield 3;
    return 4; // `return` 会结束迭代
}

let result = '';
for (const value of foo()) {
    result += value; // 只会输出 1, 2, 3
}
console.log(result); // "123"

3. 无限生成器

function* idMaker() {
    let index = 0;
    while (true) {
        yield index++;
    }
}

const genId = idMaker();
console.log(genId.next().value); // 0
console.log(genId.next().value); // 1
console.log(genId.next().value); // 2

4. yield* 委托

function* add3Gen(i) {
    yield i + 1;
    yield i + 2;
    yield i + 3;
}

function* genAdd10(k) {
    yield k;
    yield* add3Gen(k); // 委托给另一个生成器
    yield k + 10;
}

const genAdd = genAdd10(10);
console.log([...genAdd]); // [10, 11, 12, 13, 20]

5. 传递参数给生成器

function* logGenerator() {
    console.log(0);
    console.log(1, yield);
    console.log(2, yield);
    console.log(3, yield);
}

const logGen = logGenerator();
logGen.next(); // 输出 0
logGen.next('a'); // 输出 1 a
logGen.next('b'); // 输出 2 b
logGen.next('c'); // 输出 3 c

6. 生成器中的 return

function* bar() {
    yield 1;
    return 2; // 结束生成器
    yield 3; // 不会执行
}

const barGen = bar();
console.log(barGen.next()); // { value: 1, done: false }
console.log(barGen.next()); // { value: 2, done: true }
console.log(barGen.next()); // { value: undefined, done: true }

生成器的高级用法

1. 作为对象属性

const obj = {
    *generator() {
        yield 1;
        yield 2;
        yield 3;
    }
};

const objGen = obj.generator();
console.log([...objGen]); // [1, 2, 3]

2. 作为类的方法

class Tool {
    *generator() {
        yield 1;
        yield 2;
        yield 3;
    }
}

const tool = new Tool();
console.log([...tool.generator()]); // [1, 2, 3]

3. 作为计算属性

class Obj1 {
    *[Symbol.iterator]() {
        yield 1;
        yield 2;
        yield 3;
    }
}

const Obj2 = {
    *[Symbol.iterator]() {
        yield 4;
        yield 5;
        yield 6;
    }
};

console.log(Array.from(new Obj1())); // [1, 2, 3]
console.log(Array.from(Obj2)); // [4, 5, 6]

4. 控制生成器的执行

function* powers(n) {
    for (let current = n; ; current *= n) {
        yield current;
    }
}

for (const power of powers(2)) {
    if (power > 1024) break;
    console.log(power); // 2, 4, 8, 16, 32, ...
}

5. 实现 asyncawait

async/await 是基于生成器和 Promise 的语法糖,可以通过生成器函数手动实现类似的功能。以下是一个简单的实现示例:

// 示例:使用生成器实现异步逻辑 

// 定义一个生成器函数,用于模拟异步数据获取
function* fetchData() {
    // 第一次暂停点:模拟异步获取数据1
    const data1 = yield new Promise(resolve => setTimeout(() => resolve('数据1'), 1000));
    console.log(data1); // 输出数据1

    // 第二次暂停点:模拟异步获取数据2
    const data2 = yield new Promise(resolve => setTimeout(() => resolve('数据2'), 1000));
    console.log(data2); // 输出数据2

    // 返回最终结果
    return '完成';
}

// 定义一个辅助函数,用于执行生成器函数并处理异步逻辑
function asyncFunction(generatorFunc) {
    return function (...args) {
        // 创建生成器对象
        const generator = generatorFunc(...args);

        // 内部递归函数,用于处理每个 yield 返回的 Promise
        function handle(result) {
            // 如果生成器执行完成,返回最终结果
            if (result.done) return Promise.resolve(result.value);

            // 处理当前 yield 返回的 Promise
            return Promise.resolve(result.value).then(
                // 成功时,将结果传回生成器并继续执行
                res => handle(generator.next(res)),
                // 失败时,将错误传回生成器并继续执行
                err => handle(generator.throw(err))
            );
        }

        // 开始执行生成器函数
        return handle(generator.next());
    };
}

// 使用辅助函数包装生成器函数
const asyncFetchData = asyncFunction(fetchData);

// 执行异步逻辑,并处理最终结果
asyncFetchData().then(result => console.log(result)); // 输出:数据1, 数据2, 完成

通过上述代码,可以看到生成器函数如何与 Promise 配合,模拟出 async/await 的行为。


注意事项

  1. 生成器不可构造:不能通过 new 关键字实例化生成器。
    function* fn() {}
    // const instance = new fn(); // TypeError: fn is not a constructor
    
  2. yield 是暂停点:生成器函数在调用时不会立即执行,只有调用 next() 才会开始执行。
  3. yield* 委托:可以将生成器的控制权交给另一个生成器或可迭代对象。

生成器与异步编程

生成器与 Promise 结合使用,是一种强大的异步编程工具,可以解决回调地狱和控制反转的问题。通过 async/await,可以更简单地实现类似功能。


总结

生成器是 JavaScript 中非常强大的工具,适用于异步编程、迭代器实现、无限序列生成等场景。通过灵活使用 yieldyield*,可以大大提升代码的可读性和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值