带你用 Javascript 生成器玩转「会暂停」的函数

很多同学一看到 function* 和 yield 就本能地想关文章:看起来又难、又少用。

但其实,生成器(Generator)是 JS 里少见的"会暂停的函数",写好了不仅能让异步逻辑更优雅,还能在处理流式数据(比如大模型回复、SSE 等)时非常顺手。

这篇文章不讲花里胡哨的理论,而是带你一步步搞清楚:

  • 生成器到底是什么?和普通函数有什么本质区别?
  • 怎么用 next() 和 for...of 去"消费"一个生成器?
  • 在 Node.js / 浏览器里,生成器在哪些场景是真的好用?
  • 如何结合 SSE,把大模型这种流式输出封装成一个好用的异步生成器?

看完你可以不爱它,但至少不会再怕它。

1. 生成器是什么?一句话版本

用一句话概括:

生成器函数 = 可以中途 yield 暂停、之后再从上次暂停位置继续执行的函数。

它有两点和普通函数完全不一样:

  1. 不会立刻执行:调用生成器函数时,不会马上跑代码,而是返回一个"生成器对象"。
  2. 执行过程可中断 :在函数内部用 yield 抛出一个值的同时,函数会暂停;下次继续从暂停处往后执行。

生成器函数用 function* 定义,最简单的例子长这样:

javascript 复制代码
function* generator() {
    yield 1;
    yield 2;
    yield 3;
}

不管是在 Node.js 还是浏览器中,生成器都已经是非常标准的一部分了。

2. 生成器第一次调用时,到底发生了什么?

重点:调用生成器函数本身,不会执行函数体。

javascript 复制代码
function* generator() {
    yield 1;
    yield 2;
    yield 3;
}

const gen = generator(); // 这里不会执行函数体

这一步返回的 gen 是一个特殊的迭代器对象,只有在你开始"消费"它时,函数体才会真正执行。每一次消费,都通过 next() 完成。

3. 用 next() 消费生成器:一段一段地执行

生成器最常见的使用方式,就是直接调用 next():

javascript 复制代码
const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

可以看到:

  • value:本次 yield 抛出的值
  • done:生成器是否已经执行完毕
    • false:后面还有东西
    • true:已经结束,再调也是 { value: undefined, done: true }

这里有两个容易忽略的特性:

  • 同一个生成器实例(gen)只能从头到尾走一次。
  • 你可以根据自己的节奏,在任何时间点调用 next(),这就是它"可以暂停"的本质。

4. 用 for...of 优雅遍历生成器

手动 next() 有点繁琐,生成器本身就是可迭代对象,所以可以直接用 for...of 来消费:

javascript 复制代码
const gen = generator();

for (const value of gen) {
    console.log(value);
}
// 1
// 2
// 3

for...of 会在内部帮你处理 next(),直到 done 为 true 为止。所以你可以把生成器当成一个自定义规则的"数据流"来看待:你决定它一次 yield 出什么、什么时候结束。

5. 生成器能干嘛?几个典型场景

很多人印象里,生成器只和"高级用法""底层库"绑定在一起,但在 Node.js 和浏览器实际开发中,有几个场景非常适合用它:

  • 控制复杂的异步流程(早期 co 库那一代玩法)
  • 构造自定义迭代器(比如分页数据、树遍历等)
  • 处理流式数据(重点:和大模型 / SSE 这种「一小段一小段不断回来」的数据非常契合)

现在有了 async/await 之后,前两类常见程度下降了一些,但在流式场景里,生成器依然非常好用。

相关推荐
只睡四小时2 小时前
MCP 弃用 SSE?Node 分帧漏 81.7% 事件
node.js·webassembly·实时通信·mcp·streamable http
hey you~13 小时前
个体户门店办理400电话,如何对接订单系统实现来电弹窗
node.js·webhook·400电话·门店数字化·来电弹窗·订单系统对接
YIAN2 天前
RAG 学到最后,我真正学会的不是“检索”,而是让系统知道自己缺什么
langchain·node.js·llm
用户11977079480732 天前
GitHub 电商开源项目分享:用 sharp 批量出饰品主图,试了裁剪、细节图和色彩配置文件三个坑
前端·node.js
donecoding2 天前
pnpm 配好了,为什么执行的还是另一份?命令入口与运行时排查
node.js·前端工程化
江屿风2 天前
【Plain Language Large Model】【从浏览器到全栈:一课精通Node.js三剑客】流食般投喂
笔记·前端框架·npm·node.js·js·npx
SomeOtherTime2 天前
node.js基础(AI回答)
node.js
雨季mo浅忆3 天前
记录Node.js安全漏洞及其内置Brotli解码器问题
node.js·安全性测试
tommyjiatong3 天前
资安底座连环雷:从 PHP2Shell 到 Node2Shell
node.js·php
SEO_juper3 天前
Bash 交叉验证 robots.txt 和 sitemap:两个文件自相矛盾,页面永远收录不了
开发语言·python·node.js·json·bash