当前位置:首页 > 资讯 > 正文

面试官:你知道 ES6 的 生成器 Generator 吗?小明:说起 Generator,还得从我上次去餐馆点餐开始说起。

面试官:你知道 ES6 的 生成器 Generator 吗?小明:说起 Generator,还得从我上次去餐馆点餐开始说起。

这篇文章先说一个小故事,作为引子,然后系统讲讲 Generator。阅读这篇文章的话,需要一些前置知识:起码用过 Promise 和 async/await。

吃货小明去了一家菜鸟镇非常有名的餐馆——《老三样餐馆》,服务员小姐姐给了一份菜单,上面只有三道菜,从上到下列着:红烧肉,辣子鸡,兰州牛肉面。
小明都想试试,但是不知道这个餐馆的菜的饭量。但是小明是个 i 人,服务员小姐姐太漂亮了,一见到她就脸红,更别说开口问菜量了。于是小明就想,吃完一个如果还没吃饱就继续点

于是,小明就对服务员说照菜单来一个,红烧肉。服务员通知厨师做红烧肉,等了一会儿,服务员小姐姐把菜端上来了,没想到这个红烧肉这么好吃,入口即化,但是就是量太少了,压根就不够塞牙缝的。

接着,小明又对服务员说再来一道菜,辣子鸡。服务员通知厨师做辣子鸡,等了良久,服务员小姐姐把辣子鸡端上来了,小明一看,好家伙,一大盆辣子鸡,看着就很下饭,可劲儿吃,吃到最后就只剩辣椒了,可能是辣味把胃口打开了,但是还没吃饱。。

最后,小明又对服务员说再来一道,兰州牛肉面。服务员通知厨师做兰州牛肉面,等了不一会儿,服务员小姐姐就把兰州牛肉面端上来了,整整一汤碗,香味扑鼻,劲道十足,量还不少。终于让小明吃的饱饱的,还是主食垫肚子。

下饭故事到这里就结束了。用点餐来类比生成器的话,生成器就像是餐厅里的服务员。你告诉服务员你需要什么菜(通过 ),服务员记下来然后离开去厨房准备(暂停执行)。当厨房准备好这道菜时( 方法被调用),服务员会把菜送回来给你,然后等待你的下一道指令。这样,你能够根据餐桌上的需求逐步点菜(逐步生成值),而不是一次性把所有菜都端上来。

下面使用生成器结合 Promise 通过代码来模拟一下点餐过程,演示下 Generator 的用法。你可以把代码复制浏览器 F12 控制台面板直接运行,或者在 node 环境下运行。查看输出结果。

 

通过上面的小故事,我们对生成器应该有了一个基本的把握,现在系统学习一下生成器。

生成器(Generator)是 JavaScript(从 ES6 开始引入)中的一种特殊函数,它允许你创建可暂停执行的函数。在常规函数中,一旦执行开始,它就会一直运行到结束,但在生成器函数中,你可以使用 关键字暂停执行,然后在稍后的某个时刻恢复执行。这使得生成器在处理大量数据、实现异步操作、创建迭代器等方面非常有用。

生成器函数的定义使用星号 * 来标识,例如:

 

在生成器函数内部,表达式用于暂停函数的执行并返回一个值。当生成器函数被调用时,它不会立即执行,而是返回一个生成器对象。这个对象有一个 方法,每次调用 时,生成器会继续执行,直到遇到下一个 表达式,然后返回一个对象,该对象有一个 属性(包含 后的值)和一个 属性(表示是否执行完毕)。

例如,以下是一个简单的生成器示例,它生成一个序列:

 
 

这里只做列举,具体实现的细节,可以自行查阅相关资料,或者用 AI 工具,辅助学习。

  • 生成器可以用于模拟异步操作,例如读取文件、网络请求等。
  • 懒加载和流式处理。在某些前端路由库或服务器端渲染框架中,生成器可以用于构建中间件链,允许在执行过程中暂停和恢复。
  • 动画和定时任务:生成器可以用于控制动画帧,或者在间隔时间内执行任务,这样可以更好地控制时间间隔和任务执行顺序。
  • 构建工具:在构建工具如 Gulp 或 Webpack 中,生成器可以用于自定义构建流程,例如按需编译文件或处理依赖关系。
  • 状态管理:在状态管理库(如 Redux Saga)中,生成器用于处理副作用和异步操作,提供了一种优雅的方式来管理应用的状态和副作用。

这样的代码会非常不美观,影响代码的可阅读性,如果是在项目中会降低代码的可维护性。为了解决这个问题,我们可以使用的语法糖 。

可以看作是对和组合使用模式的一种优化和封装。它们都旨在改善 JavaScript 中的异步编程体验,但提供了更直接、更符合直觉的语法来处理异步操作,减少了手动管理 Promise 链的复杂性。

而且,的语法更简洁,更接近于同步代码。
await 直接表达了等待异步操作完成的意图,而 Generator 需要额外的机制(如)来迭代和处理。

所以一般情况下不推荐使用 Generator 来控制 Promise,而是使用 async/await。再拿我们的点菜的例子来看,代码可以按照下面这样改。改完之后的写法更清晰,更易读,而且我仍然可以点一个吃一个,更重要的是,我可以选择先点哪一个,比如说可以先吃辣子鸡,再吃红烧肉,最后再吃兰州牛肉面。

 
 

主要是借助这个点餐的情景,巩固 Promise 的一些常见的一些用法,这里就不一一解释了,大家可以自己试试,有个大概理解再去查阅资料系统学习。

情景一

小胖胃口大,确定能吃完,想一次性同时点红烧肉、辣子鸡、兰州牛肉面,让厨子一起给他做,那样如果人家厨子比较多的话,可以三秒就上齐菜,怎么实现呢?

 

情景二

小红到餐馆的时候,由于客人太多,有的菜原材料可能不够了,但是小红还是期望都尝一尝,哪怕就只能吃到其中一种,小红跟店家说:你都给我先安排上,没有的跟我反馈一下,有原材料的做好了都端上来。这怎么实现呢?

 
 

打印的是一个数组,里面有 3 个对象,每个对象分别对应一个 Promise,每个对象都有和属性,属性表示 Promise 的状态,属性表示 Promise 的返回值。

情景三

小先到餐馆的时候,陆续又到了两个客人,但是店家三样菜都只够各样一份的。小先比较赶时间,就跟店家说:要不这样,你们都做了吧,我要上菜最快的那一个。怎么实现呢?