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

news2024/11/19 14:25:15

讲故事学 Generator

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

小故事

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

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

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

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

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

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

/* 用Promise模拟厨师做菜,也类似前端向后端发请求的等待返回结果的过程*/

// 创建一个模拟制作红烧肉的Promise,表示菜品制作需要2秒
const dish1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("花了2秒,做好了红烧肉");
  }, 2000);
});

// 创建一个模拟制作辣子鸡的Promise,表示菜品制作需要3秒
const dish2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("花了3秒,做好了辣子鸡");
  }, 3000);
});

// 创建一个模拟制作兰州牛肉面的Promise,表示菜品制作需要1秒
const dish3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("花了1秒,做好了兰州牛肉面");
  }, 1000);
});

/**
 * 生成器函数,用于生成依次制作的菜品Promise
 * @returns {Object} 生成器对象,每次yield返回一个Promise
 */
function* dishGenerator() {
  yield dish1;
  yield dish2;
  yield dish3;
}

// 初始化生成器,相当于叫来了一个服务员
let genP = dishGenerator();

// 启动菜品制作流程,依次处理每个Promise
genP.next().value.then(res => {
  // 输出第一道菜制作完成的信息
  console.log(res);
  // 继续制作第二道菜
  genP.next(res).value.then(res => {
    // 输出第二道菜制作完成的信息
    console.log(res);
    // 继续制作第三道菜
    genP.next(res).value.then(res => {
      // 输出第三道菜制作完成的信息
      console.log(res);
    });
  });
});

// 输出结果:
// 花了2秒,做好了红烧肉
// 花了3秒,做好了辣子鸡
// 花了1秒,做好了兰州牛肉面

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

介绍生成器(Generator)

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

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

function* myGenerator() {
  // ...
}

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

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

function* generateNumbers(n) {
  for (let i = 1; i <= n; i++) {
    yield i;
  }
}

let generator = generateNumbers(5);
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
// ...
console.log(generator.next()); // { value: undefined, done: true }

生成器的应用场景

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

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

async/await 解决回调地域

我们在上面使用生成器(Generator)和 Promise 组合的方式实现了模拟点餐的代码,其中为了依次获取 Promise 的异步回调的执行结果 resolve中的值,我们写了一个类似套娃的回调地域
在这里插入图片描述

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

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

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

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

// 创建一个模拟制作红烧肉的Promise,表示菜品制作需要2秒
const dish1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("花了2秒,做好了红烧肉");
  }, 2000);
});

// 创建一个模拟制作辣子鸡的Promise,表示菜品制作需要3秒
const dish2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("花了3秒,做好了辣子鸡");
  }, 3000);
});

// 创建一个模拟制作兰州牛肉面的Promise,表示菜品制作需要1秒
const dish3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("花了1秒,做好了兰州牛肉面");
  }, 1000);
});

// 立即执行函数
(async () => {
  const res1 = await dish2;
  console.log(res1);
  const res2 = await dish1;
  console.log(res2);
  const res3 = await dish3;
  console.log(res3);
})();

最后小小拓展一下

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

情景一

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

(async () => {
  const resultAll = await Promise.all([dish1, dish2, dish3]);
  console.log(resultAll); // 输出结果['花了2秒,做好了红烧肉', '花了3秒,做好了辣子鸡', '花了1秒,做好了兰州牛肉面']
})();

情景二

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

(async () => {
  const resultAllSettled = await Promise.allSettled([dish1, dish2, dish3]);
  console.log(resultSettle);
})();

在这里插入图片描述

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

情景三

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

(async () => {
  const resultRace = await Promise.race([dish1, dish2, dish3]);
  console.log("最快的:", resultRace); //最快的: 花了1秒,做好了兰州牛肉面
})();

最后

文章系统介绍了 ES6 的生成器 Generator,包含精心设计的故事和实例代码,既生动有趣,有可操作性,也不失深度。创作不易,有收获的话,可以点个赞哟,欢迎留言交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1791796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何修改cPanel面板的语言

本周有一个客户&#xff0c;购买Hostease的主机&#xff0c; 客户购买的是Linux虚拟主机&#xff0c;带cPanel面板的。询问我们的在线客服&#xff0c;他想修改cPanel面板的默认语言。Hostease虚拟主机默认英语&#xff0c;客户想要修改成中文。 在cPanel面板中修改语言设置是一…

3 - 大的国家(高频 SQL 50 题基础版)

3.大的国家 -- 查询属性&#xff1a;国家名称、人口和面积 select name,population,area fromWorld where area>3000000 OR population>25000000;

React中常见的面试题

本文是结合实践中和学习技术文章总结出来的笔记(个人使用),如有雷同纯属正常((✿◠‿◠)) 喜欢的话点个赞,谢谢! 1. 约束性组件与非约束性组件 1.1. 非约束性组件 非约束性组件其实就是不能控制状态的组件,比如: <input type"text" defaultValue"123&qu…

一个思维狂赚20万+?揭秘电商平台隐藏的流量认知!

你想要的流量&#xff0c;资源&#xff0c;人脉&#xff0c;都已经有人为你准备&#xff0c;你只需要找到拥有这些资源的人。对于流量和信息&#xff0c;也是一样&#xff0c;你想找的客户和产品&#xff0c;都已经有人为你准备在淘宝、拼多多等电商平台&#xff0c;你只需要找…

【踏雪无痕的痕六】——数学中有意思的问题

一、背景介绍 提出一个问题往往比解决一个问题更有意义&#xff0c;因为提出一个问题相当于提出了一个思考问题的维度&#xff1b;而解决一个问题是沿着这个维度将已有的知识串起来的过程 三、过程 1.数人数你会吗&#xff1f; 小名再第10位&#xff0c;小李再第15位&#…

VRTK4.0学习——(二)

手柄绑定以及显示 1.导入CameraRigs.UnityXRPluginFramework 和 CameraRigs.TrackedAlias 预设&#xff0c;将CameraRigs.UnityXRPluginFramework拖入CameraRigs.TrackedAlias的Elements中即可&#xff0c;运行软件后即可看到手柄了 注&#xff1a;如果无法看到手柄&#xff…

AI降痕工具使用指南:如何有效降低AIGC疑似度

随着人工智能技术的突飞猛进&#xff0c;AI生成内容&#xff08;AIGC&#xff09;已被广泛用于学术论文撰写中&#xff0c;提高效率同时也带来了原创性的挑战。面对日益严格的学术审查&#xff0c;一个突出的问题是&#xff1a;使用AI代写的论文能否通过内容检测&#xff1f;因…

《精品生活》万方普刊投稿发表简介

《精品生活》杂志是由国家新闻出版总署批准&#xff0c;南方出版传媒股份有限公司主管&#xff0c;广东大沿海出版工贸有限公司主办&#xff0c;广东精品生活杂志社出版的综合性文化期刊。主要栏目&#xff1a;教学研究、艺术教育、文化广角、民族文化、理论前沿、综合论坛。 刊…

生命周期钩子小案例

文章目录 一、在created中发送数据二、在mounted中获取焦点 一、在created中发送数据 <body><div id"app"><ul><li v-for"(item, index) in list" :key"item.id" class"news"><div class"left"…

从功能性磁共振成像(fMRI)数据重建音频

听觉是人类最重要的感官之一&#xff0c;它负责接收外部的听觉刺激&#xff0c;并将这些信息传递给大脑进行处理和理解。研究人员正致力于从神经科学和计算机科学两个领域探索人脑的听觉感知机制。一个关键目标是从人脑中解码神经信息&#xff0c;并重建原始的刺激。常见的大脑…

用友BIP收入云:助力旅游行业实现高效收入自动化,驱动收入增长

在数字化浪潮的推动下&#xff0c;旅游行业正经历着前所未有的变革。从传统的线下服务模式到线上线下融合&#xff0c;再到如今的智能化、自动化管理&#xff0c;每一步都标志着旅游行业向更高效、更精准、更便捷的方向发展。其中&#xff0c;收入管理作为旅游企业运营的核心环…

机器学习:更多关于元学习

目录 Meta Learning vs Self-supervised Learning 自监督学习——找初始化的参数MAML 自动学出合适的参数 MAML&#xff1a;不断的学初始化参数MAML的初始化参数来自BERT MAML&#xff1a;找出来的初始化参数能在训练任务上表现的很好BERT&#xff1a;自监督目标是不同的下游任…

msvcp140.dll是什么dll文件?msvcp140.dll文件的丢失要怎么去修复?

msvcp140.dll是什么dll文件&#xff1f;一般会问出这种问题的人&#xff0c;都是遇到了msvcp140.dll丢失的情况了&#xff0c;这时候你的一些程序是打不开的&#xff0c;你需要修复好msvcp140.dll文件才可以正常的打开程序&#xff0c;今天我们就来了解一下msvcp140.dll这文件&…

KT1025A的双模蓝牙芯片,参考标准蓝牙天线,蓝牙距离短,会卡

一、问题简介 使用KT1025A的双模蓝牙芯片&#xff0c;为什么我参考BT201或者BT301&#xff0c;或者BT321F设计的蓝牙天线&#xff0c;蓝牙距离短&#xff0c;会卡等等&#xff0c;这个可能是什么原因&#xff0c;如何改善呢&#xff1f; 问题详细分析 首先看看客户的板子PCB…

opencv快速安装以及各种查看版本命令

安装opencv并查看其版本&#xff0c;直接通过一个可执行文件实现。 #!/bin/bashwget https://codeload.github.com/opencv/opencv/zip/3.4 -O opencv-3.4.zip && unzip opencv-3.4.zip && cd opencv-3.4 && \mkdir build && cd build &&a…

C++--DAY3

思维导图 设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数。 #include <iostream>using namespace std; class …

【文档智能】符合人类阅读顺序的文档模型-LayoutReader原理及权重开源

引言 阅读顺序检测旨在捕获人类读者能够自然理解的单词序列。现有的OCR引擎通常按照从上到下、从左到右的方式排列识别到的文本行&#xff0c;但这并不适用于某些文档类型&#xff0c;如多栏模板、表格等。LayoutReader模型使用seq2seq模型捕获文本和布局信息&#xff0c;用于…

品牌营销的“必杀技”,一文带你看懂如何实现精准营销

你在遇到疑惑寻求解决方法时是否会优先想到“百度一下”&#xff1f;我们脑中的优先选择其实就是品牌将自身特点结合其目标用户信息采取精准营销从而完成的广告信息的成功投放。而精准营销&#xff0c;作为一种现代化的广告营销方式&#xff0c;已成为品牌营销的新趋势&#xf…

varchar 字段扩展问题

背景 近期接到一个产品需求&#xff0c;由于上游业务字段扩大了字段&#xff0c;下游的字段也得跟着调整扩大&#xff0c;这就涉及几十张大表&#xff0c;十几亿行数据的变更。 如果按照传统方式 onlie-ddl 借用第三方工具也得三四天分批跑&#xff0c;看了看MySQL官网&#…

0605_C++3

练习1&#xff1a; 设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数 #include <iostream>using namespace std; …