JavaScript Generator函数使用分析

作者:青柠时间:2026-10-26 06:29:42浏览:209

本文深入解析JavaScript中的Generator函数,阐述其通过yield关键字实现暂停与恢复执行的核心机制。详细讲解next方法的返回值、向Generator传参的技巧,以及通过yield*实现函数嵌套。最后结合Promise探讨如何利用Generator处理异步网络请求,并引出async/await的演进背景。

1. Generator的定义和执行

如果Promise是为了解决回调地狱而诞生的,那么Generator函数则是为了解决异步编程问题而出现的。

普通函数在调用后会立即执行完毕,而Generator函数具有暂停执行的能力,不会一次性执行完所有代码。正是这种特性使其能够用于处理异步操作。

定义Generator函数的方式与普通函数类似,区别在于*function关键字和函数名之间有一个星号。

调用Generator函数并不会立即执行函数体内的代码,而是返回一个迭代器对象。必须通过该对象的next方法来逐步执行代码。例如:

function* gen() {
    console.log(1)
}
// 定义迭代器对象
const iterator = gen()
iterator.next() // 如果不执行这一局代码,1不会被打印

当next方法执行时,遇到yield语句就会暂停,直到再次调用next方法继续执行。示例如下:

function* gen() {
    yield 1
    console.log('A')
    yield 2
    console.log('B')
    yield 3
    console.log('C')
    return 4
}
// 定义迭代器对象
const iterator = gen()
iterator.next() // 执行 gen 函数,打印为空,遇到 yield 1 停止执行
iterator.next() // 继续执行函数,打印 A,遇到 yield 2 停止执行
iterator.next() // 继续执行函数,打印 B,遇到 yield 3 停止执行
iterator.next() // 继续执行函数,打印 C

1. Generator的定义和执行

next方法的返回值包含yield后的值或函数的return值。例如:

// 同步代码
function* gen() {
    yield 1
    console.log('A')
    yield 2
    console.log('B')
    yield 3
    console.log('C')
    return 4
}
// 定义迭代器对象
const iterator = gen()
// 异步代码
console.log(iterator.next()) // 打印为空  next返回 {value:1,done:false}
console.log(iterator.next()) // A  next返回 {value:2,done:false}
console.log(iterator.next()) // B  next返回 {value:3,done:false}
console.log(iterator.next()) // C  next返回 {value:4,done:true},如果函数有return值,最后一个next方法,它的value值为return的值 value:4;如果没有。值为 undefined

1. Generator的定义和执行

补充说明:Generator之所以能将异步操作转化为同步风格,是因为在Generator函数内部只需编写同步逻辑,实际的异步操作由迭代器对象执行。在复杂业务中,大量使用迭代器处理异步会导致代码不够优雅,因此ES7引入了async/await方案。该方案允许只编写同步风格的代码,底层封装了异步操作,使代码更加简洁优雅。

2. Generator中yield在赋值号左边的情况

当yield位于等号右边时,其返回值不会赋给左边的变量,而是返回给next方法。

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);
}
const iterator = gen()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next()) 

1. Generator的定义和执行

这是因为Generator函数在遇到yield时已暂停,尚未执行赋值操作。只有再次调用next方法后,才会继续向下执行赋值。若希望r1/r2/r3获得值,可以通过next方法传入参数,如下所示:

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);
}
const iterator = gen()
iterator.next() // 第一个 next 方法不用给值,即使给值也不会生效
iterator.next('A')
iterator.next("B")
iterator.next('C')

1. Generator的定义和执行

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}

1. Generator的定义和执行

4. 使用generator函数完成网络请求

// 使用generator来完成异步网络请求,它还是要利用到promise
// 模拟网络请求
function request(num = 1) {
    return new Promise((resolve, reject) => {
        return setTimeout(() => {
            resolve(++num)
        }, 1000);
    })
}
// generator函数中的代码,发起的网络请求它就类似于同步写法
function* gen(num) {
    // yield右侧是一个promise对象
    let r1 = yield request(10)
    console.log('r1', r1);
    let r2 = yield request(r1)
    console.log('r2', r2);
    let r3 = yield request(r2)
    console.log('r3', r3);
}
const iterator = gen(10)
iterator.next().value.then(ret1 => {
    iterator.next(ret1).value.then(ret2 => {
        iterator.next(ret2).value.then(ret3 => {
            iterator.next(ret3)
        })
    })
})

1. Generator的定义和执行

上述写法在涉及多个网络请求时,异步部分代码会变得复杂。可以通过co库中的迭代函数进行优化:

// 使用generator来完成异步网络请求,它还是要利用到promise
// 模拟网络请求
function request(num) {
    return new Promise((resolve, reject) => {
        return setTimeout(() => {
            resolve(++num)
        }, 1000);
    })
}
// generator函数中的代码,发起的网络请求它就类似于同步写法
function* gen(num) {
    // yield右侧是一个promise对象
    let r1 = yield request(10)
    console.log('r1', r1);
    let r2 = yield request(r1)
    console.log('r2', r2);
    let r3 = yield request(r2)
    console.log('r3', r3);
    let r4 = yield request(r3)
    console.log('r4', r4);
    let r5 = yield 'ok'
    console.log('r5', r5);
}
// 通过co库实现
function co(generator, ...params) {
    const iterator = gen(...params)
    // 迭代函数
    const next = n => {
        let { value, done } = iterator.next(n)
        // 判断一下value它是一个promise对象,如果不是promise对象则需要手动转为promise对象,或抛异常
        if (value != undefined && typeof value.then != "function") {
            throw new Error('必须为promise对象')
            // value = Promise.resolve(value)
        }
        if (done) return;
        // value.then(ret => next(ret))
        value.then(next)
    }
    next(0)
}
co(gen, 100)

1. Generator的定义和执行

本文关于JavaScript Generator函数使用分析的介绍到此结束。如需了解更多JS Generator相关内容,请查阅本站往期文章或继续浏览后续文章,感谢支持!

作者最新文章