news 2026/5/30 20:19:14

零基础掌握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/5/28 18:05:34

YOLOFuse Biendata平台集成测试成功

YOLOFuse Biendata平台集成测试成功 在夜间安防监控的实战场景中,一个常见的困境是:可见光摄像头在无光环境下几乎“失明”,而红外图像虽能捕捉热源却缺乏纹理细节。如何让AI系统像人眼一样,在黑夜中既“看得见”又“认得清”&…

作者头像 李华
网站建设 2026/5/28 18:06:12

无监督核谱回归测试(UKSRtest)函数详解与MATLAB实现

引言 在机器学习中,降维是处理高维数据的关键步骤,尤其是当数据呈现非线性结构时,核方法(如核主成分分析)能有效捕捉潜在模式。无监督核谱回归(Unsupervised Kernel Spectral Regression, UKSR) 是一种高效的核化降维技术,它通过谱回归框架实现无监督学习,类似于核LP…

作者头像 李华
网站建设 2026/5/30 18:00:19

YOLOFuseDiscord社区筹建中:欢迎加入讨论群

YOLOFuse多模态目标检测技术解析:基于Ultralytics YOLO的双流融合实现 在智能安防、自动驾驶和夜间监控等场景中,环境感知系统常常面临低光照、烟雾遮挡或极端天气带来的挑战。仅依赖可见光图像的传统目标检测模型,在这些条件下性能急剧下降…

作者头像 李华
网站建设 2026/5/30 13:40:33

互联网大厂Java求职面试模拟实战:谢飞机的三轮提问与详细解答

互联网大厂Java求职面试模拟实战:谢飞机的三轮提问与详细解答 在互联网大厂的Java求职面试中,技术问答不仅考察基础能力,更关注实际业务场景下的技术应用。本文以面试官与水货程序员谢飞机的对话形式,展现三轮问答过程与详尽答案解…

作者头像 李华
网站建设 2026/5/29 17:15:50

YOLOFuse百度搜索收录情况监测:首页排名提升

YOLOFuse:多模态目标检测的高效落地实践 在夜间监控、烟雾环境感知或边境巡逻等现实场景中,传统基于可见光的目标检测系统常常“失明”——光线不足、遮挡严重时,模型性能急剧下降。而与此同时,红外传感器却能捕捉到物体的热辐射信…

作者头像 李华
网站建设 2026/5/30 12:01:55

YOLOFuseProduct Hunt产品发布日冲榜成功

YOLOFuse:多模态目标检测的开箱即用解决方案 在城市夜间安防系统中,一个常见的尴尬场景是:监控摄像头在白天表现优异,一旦进入黄昏或浓雾环境,画面迅速陷入模糊与漏检——可见光传感器失去了对低对比度目标的捕捉能力…

作者头像 李华