生成器函数是带暂停能力的函数,调用后返回实现迭代协议的Generator对象,代码在每次next()调用时执行到yield处暂停;yield让出控制权并返回值,yield*可委托迭代,支持惰性求值。

生成器函数本质是带暂停能力的函数
它不是普通函数,调用后不立即执行,而是返回一个 Generator 对象 —— 这个对象本身就是一个可迭代协议(Symbol.iterator)和迭代器协议(next() 方法)的实现体。关键在于:函数体内的代码只在每次调用 next() 时执行到下一个 yield 处并暂停。
yield 不是返回值,是“让出控制权”的指令
每次遇到 yield,函数暂停,把右侧表达式的值作为 value 返回,并将状态冻结;下次调用 next() 时从暂停处继续,且可接收传入的参数作为上一个 yield 表达式的返回值(注意:首次 next() 传参无效)。
-
yield只能在生成器函数内使用,否则语法错误 - 不能在箭头函数中使用
yield -
yield后面可以跟任意表达式,包括函数调用、Promise、甚至另一个yield*委托 - 函数末尾隐式返回
{ value: undefined, done: true }
function* countdown(n) {
while (n > 0) {
yield n;
n--;
}
}
const gen = countdown(3);
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: undefined, done: true }用 yield* 委托迭代,避免手动遍历
当需要把另一个可迭代对象(数组、字符串、其他生成器等)的值“内联”进当前生成器时,yield* 比循环 + yield 更简洁、更符合语义,且自动处理 return 和 throw 传递。
-
yield* iterable会逐个yielditerable的每个值 - 如果
iterable是另一个生成器,它的return值会成为yield*表达式的返回值 - 对数组、Set、Map 使用
yield*是合法的,因为它们实现了迭代协议
function* numbers() {
yield 1;
yield* [2, 3];
yield* "45";
}
Array.from(numbers()); // [1, 2, 3, '4', '5']惰性求值的真实价值在“不提前计算”和“按需中断”
生成器常被误认为只是“写法花哨的数组”,但它真正不可替代的地方在于:数据源可能无限、开销巨大或依赖副作用(如 I/O),而你只需要前几个结果。
立即学习“Java免费学习笔记(深入)”;
- 无限序列(斐波那契、素数筛)不会卡死,只算你要的那几项
- 读取大文件时,每
yield一行,内存只存当前行,而非全部加载 - 配合
for...of或展开运算符[...gen]时,一旦外部逻辑break或异常退出,生成器内部未执行的代码就不会运行 - 注意:
Array.from(gen)或[...gen]仍会遍历全部,除非你手动控制next()
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
// 只取前 5 项,后面完全不计算
const first5 = [];
const fib = fibonacci();
for (let i = 0; i < 5; i++) {
first5.push(fib.next().value);
}生成器的暂停/恢复机制底层依赖执行上下文栈的保存与还原,不是靠定时器或异步队列;这意味着它天然同步、零调度开销,但也意味着不能直接 await 一个 yield —— 需要 async function* 配合 yield 返回 Promise,再由消费者用 for await...of 消费。这点容易混淆,也最容易漏掉错误处理。











