news 2026/6/10 15:18:23

零基础掌握ES6语法:Generator函数入门示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
零基础掌握ES6语法:Generator函数入门示例

从“暂停”开始理解JavaScript:Generator函数的实践与思考

你有没有想过,一个函数执行到一半能停下来,等你想让它继续的时候再接着运行?这听起来像是科幻电影里的桥断点续传,但在 JavaScript 中,这种能力真实存在——它就是Generator 函数

在 ES6(ECMAScript 2015)发布之前,JavaScript 的函数一旦调用,就会从头跑到尾,中间无法暂停。回调嵌套层层叠加,“回调地狱”让代码变得难以维护。而 Generator 的出现,打破了这一限制。它不是简单地改进语法,而是为语言引入了一种全新的执行模型:协程(Coroutine)。


为什么需要“可暂停”的函数?

想象这样一个场景:你要依次加载用户信息、用户的订单列表和推荐商品数据。传统写法可能是这样的:

fetchUser((user) => { fetchOrders(user.id, (orders) => { fetchRecommendations(user.pref, (recs) => { renderDashboard(user, orders, recs); }); }); });

缩进越来越深,逻辑分散,错误处理几乎无从下手。

后来 Promise 出现了,写法变成链式调用:

fetchUser() .then(user => Promise.all([user, fetchOrders(user.id), fetchRecommendations(user.pref)])) .then(([user, orders, recs]) => renderDashboard(user, orders, recs));

虽然好了一些,但还是不够直观。

我们真正想要的是像写同步代码一样组织异步流程:

const user = yield fetchUser(); const orders = yield fetchOrders(user.id); const recs = yield fetchRecommendations(user.pref); renderDashboard(user, orders, recs);

看起来是不是清晰多了?而这,正是 Generator + 执行器所能实现的效果。


Generator 是什么?用最简单的例子讲清楚

Generator 函数通过function*定义,调用后不会立即执行,而是返回一个遍历器对象(Iterator)。你可以手动控制它的每一步执行。

function* helloWorld() { yield 'Hello'; yield 'World'; return 'Ended'; }

现在我们来“驱动”这个函数一步步运行:

const gen = helloWorld(); console.log(gen.next()); // { value: 'Hello', done: false } console.log(gen.next()); // { value: 'World', done: false } console.log(gen.next()); // { value: 'Ended', done: true }

每次调用.next(),函数就向前走一步,直到遇到下一个yieldreturn
-value表示当前产出的值;
-done表示是否已结束。

这就像你按下了播放键的录音机,每按一次快进,播放一句。


核心机制:不只是“暂停”,还能双向通信

普通函数只能单向输出结果(return),而 Generator 支持双向数据流动

看这个例子:

function* echoMachine() { const a = yield 'Ready for input?'; console.log('Got:', a); const b = yield 'Another one?'; console.log('Got:', b); return 'Done.'; }

启动并传入数据:

const it = echoMachine(); it.next(); // 启动,停在第一个 yield → { value: 'Ready...', done: false } it.next('Start!'); // 把 'Start!' 赋给 a → { value: 'Another...', done: false } it.next('Go ahead'); // 把 'Go ahead' 赋给 b → { value: 'Done.', done: true }

注意关键点:.next(data)中的数据会作为上一个yield表达式的返回值

这就意味着,外部可以影响内部逻辑流程。这种能力,在构建复杂状态流转或中间件系统时极为强大。


实战一:做一个无限计数器,却不卡死浏览器

利用惰性求值特性,我们可以轻松创建看似“无限”的序列,而不会阻塞主线程。

function* counter() { let n = 0; while (true) { yield ++n; } }

使用时按需取值:

const c = counter(); console.log(c.next().value); // 1 console.log(c.next().value); // 2 console.log(c.next().value); // 3

尽管是while(true),但由于每次只执行到yield就暂停,所以完全安全。类似思路可用于生成斐波那契数列、分页数据流、动画帧控制器等场景。


实战二:用 Generator 实现树结构中序遍历

对于非线性数据结构,比如二叉树,传统的递归遍历容易造成内存堆积。而 Generator 可以做到边访问边产出,节省资源。

class TreeNode { constructor(value, left = null, right = null) { this.value = value; this.left = left; this.right = right; } *inOrder() { if (this.left) yield* this.left.inOrder(); // 委托子Generator yield this.value; if (this.right) yield* this.right.inOrder(); } }

使用方式和数组一样自然:

const root = new TreeNode(10, new TreeNode(5), new TreeNode(15) ); for (const val of root.inOrder()) { console.log(val); // 输出:5 → 10 → 15 }

这里用了yield*,它可以将另一个可迭代对象的产出“转发”出来,非常适合递归结构。


实战三:用 Generator 组织异步流程,告别回调地狱

这是 Generator 最具革命性的应用场景之一。虽然现在有async/await,但理解它是如何基于 Generator 演进而来的,非常重要。

先定义一个模拟异步请求的函数:

function fetchData(url) { return new Promise(resolve => { setTimeout(() => resolve(`Data from ${url}`), 1000); }); }

然后用 Generator 写出“同步风格”的异步逻辑:

function* asyncFlow() { console.log('Fetching user...'); const user = yield fetchData('/api/user'); console.log('Fetching posts...'); const posts = yield fetchData(`/api/posts?uid=${user.id}`); return { user, posts }; }

但这段代码不会自动运行。我们需要一个“执行器”来驱动它:

function run(generatorFunc) { const iterator = generatorFunc(); function handle(result) { if (result.done) return Promise.resolve(result.value); return Promise.resolve(result.value).then(data => { return handle(iterator.next(data)); }); } return handle(iterator.next()); }

最后启动:

run(asyncFlow).then(console.log);

你会发现,整个过程像极了今天的async/await。事实上,co库就是这么干的,而redux-saga和早期Koa框架也都依赖这一模式。


Generator 在现代框架中的身影

别以为 Generator 已经过时了。恰恰相反,它在很多高级工具中扮演着底层角色。

Redux-Saga:用 Generator 管理副作用

function* loginSaga() { try { const credentials = yield take('LOGIN_REQUESTED'); const token = yield call(loginAPI, credentials); yield put({ type: 'SET_AUTH_TOKEN', token }); const profile = yield call(fetchProfile); yield put({ type: 'SET_PROFILE', profile }); yield call(saveToLocal, { token, profile }); } catch (err) { yield put({ type: 'LOGIN_ERROR', error: err.message }); } }

这里的take,call,put都是 effect 创建函数,配合 middleware 解释执行。整个流程顺序清晰、易于测试、支持取消和竞态处理。

Koa.js:比 Express 更优雅的中间件模型

Koa 1.x 版本完全基于 Generator 实现中间件:

app.use(function *(next) { console.log('Before'); yield next; console.log('After'); });

相比 Express 的next()回调模式,Koa 利用 Generator 实现了真正的“洋葱模型”,逻辑更直观。


使用建议与避坑指南

尽管功能强大,但使用 Generator 仍需谨慎:

✅ 推荐使用场景

  • 构建自定义迭代器(如遍历图、DOM 树)
  • 实现有限状态机(如表单验证、游戏 AI)
  • 编排复杂的异步流程(尤其在 Redux-Saga 中)
  • 生成大数据流或无限序列(惰性加载)

❌ 不推荐滥用的情况

  • 简单的异步操作优先用async/await
  • 避免在.next()中进行大量同步计算,防止阻塞主线程
  • IE 全系列不支持,必须通过 Babel 转译才能使用
  • 调试体验较差,Chrome DevTools 对 Generator 的断点支持不如普通函数流畅

Generator 的意义:不止是语法糖

很多人说:“现在都用async/await了,还学 Generator 干嘛?”
其实不然。

Generator 是理解 JavaScript 异步演进的关键桥梁。没有它,就不会有co,不会有redux-saga,也不会催生出async/await的设计灵感。

更重要的是,它教会我们一种思维方式:把复杂流程拆解成可控的小步骤。无论是处理事件流、管理状态切换,还是实现懒加载算法,这种“分步推进”的思想都能带来更清晰的设计。

当你看到一段yield call(api)的代码时,你不只是在读一行语法,而是在观察一个暂停—恢复—传递数据的精密协程系统正在运行。


结语:通往协程世界的入口

Generator 函数或许不再是日常开发的首选,但它所承载的理念远未过时。它打开了 JavaScript 对协程生成式编程的大门,让我们第一次真正拥有了对函数执行流的精细控制权。

如果你正在学习前端工程化、深入状态管理、或是研究响应式编程,那么理解 Generator,就是在打牢地基。

下一次当你写出async/await的时候,不妨想一想:背后那个曾被用来驱动异步流程的 Generator,是如何一步步引领我们走到今天的。

如果你在项目中用过redux-saga或亲手写过 Generator 执行器,欢迎在评论区分享你的实战经验!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 11:30:03

YOLOFuse零售门店客流分析:昼夜连续统计无盲区

YOLOFuse零售门店客流分析:昼夜连续统计无盲区 在一家24小时营业的便利店中,凌晨两点的监控画面里,灯光微弱,货架之间的过道几乎被黑暗吞没。传统的摄像头系统早已“失明”,但店长仍需要知道这一时段有多少顾客进出——…

作者头像 李华
网站建设 2026/6/10 11:04:28

YOLOFuse Flask服务包装示例代码分享

YOLOFuse Flask服务包装示例代码分享 在智能安防、自动驾驶和夜间监控等实际场景中,单一可见光摄像头在低光照或烟雾遮挡环境下常常“力不从心”——行人模糊不清、车辆轮廓难辨。这时候,红外(IR)相机的优势就显现出来了&#xff…

作者头像 李华
网站建设 2026/6/10 12:39:36

互联网大厂Java求职者面试故事与技术深度解析

互联网大厂Java求职者面试故事与技术深度解析 引言 在互联网大厂的Java岗位面试中,技术面试不仅考察候选人的基础知识,更注重业务场景的理解与实际代码能力。本文以一个精彩的面试对话故事作为主线,通过面试官与被称为"谢飞机"的程…

作者头像 李华
网站建设 2026/6/10 11:09:09

YOLOFuse微博话题运营:#多模态目标检测#热度上升

YOLOFuse微博话题运营:#多模态目标检测#热度上升 在夜间监控、火灾现场或浓雾环境中,摄像头拍到的画面常常模糊不清——可见光图像失去细节,传统AI模型“看不清”也就“认不出”。然而,红外相机却能捕捉物体的热辐射信息&#xf…

作者头像 李华
网站建设 2026/6/10 11:10:54

YOLOFuse Azure云服务兼容性测试结果公布

YOLOFuse Azure云服务兼容性测试结果公布 在智能安防、夜间监控和自动驾驶等实际场景中,单一可见光图像的检测能力常常受限于光照条件。当面对黑夜、雾霾或遮挡环境时,传统RGB目标检测模型的表现往往大幅下滑。而红外(IR)成像能够…

作者头像 李华
网站建设 2026/6/10 13:20:19

解决Screen to Gif在Win10/Win11常见问题

Screen to Gif 在 Win10/Win11 上总黑屏、没声音、打不开?一文彻底解决! 你是不是也遇到过这种情况:兴冲冲打开 Screen to Gif ,准备录个操作教程发给同事,结果一点“录制”按钮——画面全黑;或者更糟&a…

作者头像 李华