JavaScript Generator函数使用分析
本文深入解析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
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
补充说明: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()) 
这是因为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')
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}
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)
})
})
})
上述写法在涉及多个网络请求时,异步部分代码会变得复杂。可以通过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)
本文关于JavaScript Generator函数使用分析的介绍到此结束。如需了解更多JS Generator相关内容,请查阅本站往期文章或继续浏览后续文章,感谢支持!
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
