JavaScript系列——Generator

文章目录

    • 概要
    • 使用示例
      • 手动迭代
      • for循环自动迭代
    • Promise 与 Generator对比
      • 使用async +await 处理promise
      • 使用generator 处理promise
      • 手动使用 generator 实现 async+await 效果
        • generator 生成函数如下
        • 手动实现核心代码
        • 执行
    • 小结

概要

Generator,是ES6新增的特性,generator 实例是由 生成器函数 生成符合迭代协议迭代器协议、可以手动控制迭代步骤的对象

Generator 是隐藏类Iterator 的子类 。在生成器内部,可以通过yield 来返回每一个操作步骤需要返回的内容,然后调用实例的next 方法,返回对应步骤的返回值。

可以理解为Generator 是一个步骤机,通过yield依次 定义每一个步骤,然后通过next 启动每一个步骤。调用next 不会执行全部的yield 语句。

执行一次next 只会执行一次yield语句,这个就是其神奇之处。

使用示例

手动迭代

下面是generator的简单示例

function* generator() {yield 1;yield 2;yield 3;
}const gen = generator(); // "Generator { }"console.log(gen.next()); //  { value: 1, done: false }
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3

和普通函数有所区别的是,generator 的生成函数,要在function 关键字加* 来与普通函数区分开来。

执行 generator 便可以得到一个generator 对象。通过next()方法可以得到一个对象,里面包含valuedone属性,其中value 的值,就是一个yield语句对应的值,done 表示generator对象是否执行完所有的步骤

for循环自动迭代

除此用法之外,我们还可以配合for 循环,自动迭代generator 对象,如下代码:

const foo = function* () {yield 'a';yield 'b';yield 'c';
};let str = '';
for (const val of foo()) {str = str + val;
}console.log(str);
// Expected output: "abc"

Promise 与 Generator对比

在之前的文章的中,Promise的介绍
介绍了Promise的特性,现在总结一下Promise的特性

  • 不可取消和暂停,Promise 一旦创建,无法停止。
  • 状态不可控,Promise 的状态控制权由异步任务决定,具体执行到哪里也无法决定。

generator有以下特点

  • 可以决定状态走到哪一步,可以随时停止步骤继续执行和继续执行

在介绍异步的时候,介绍过使用 async + await的方式处理promise,代码可以更加有可读性,其实async +await 就是 generator 的语法糖,其内部就是基于generator 的自动执行实现的。

使用async +await 处理promise

let p = function (val) {return new Promise((resolve) => {setTimeout(() => {resolve(val)}, 1000);})
}async function testAsync() {const data1 = await p(1)console.log(data1)const data2 = await p(2)console.log(data2)const data3 = await p(3)console.log(data3)
}testAsync()

上面的代码可以实现,间隔一秒打印1,2,3

下面我们使用 generator 来实现以上的效果

使用generator 处理promise

function* testG() {const data1 = yield p(1)console.log(data1)const data2 = yield p(2)console.log(data2)const data3 = yield p(3)console.log(data3)
}let gen = testG()let dataPromise = gen.next().value // 返回对象中的 value 值才是一个 promisedataPromise.then((val) => {let data2Promise = gen.next(val).valuedata2Promise.then((val2) => {let data3Promise = gen.next(val2).valuedata3Promise.then((val3) => {gen.next(val3)})})})
// 按序每隔一秒打印 1、2、3

上诉代码可以实现同样的效果,但出现了回调地狱。

手动使用 generator 实现 async+await 效果

generator 生成函数如下
const getData = () => new Promise(resolve => setTimeout(() => resolve("data"), 1000))
//间隔一秒打印
function* testG() {const data = yield getData()console.log('data: ', data);const data2 = yield getData()console.log('data2: ', data2);return 'success'
}
手动实现核心代码
function asyncGenerator(generatorFunc){//自动执行generator 步骤//如何自动执行,需要满足以下条件,// 执行next ,判断done状态,// 报错抛出异常return function(){//创建generator const gen = generatorFunc.apply(this,arguments)return new Promise((resolve,reject)=>{//创建step 函数function step(key,arg){let generatorResult;try{generatorResult = gen[key](arg)}catch(error){return reject(error)}const {done,value} = generatorResult//判断generator的done状态,true 表示执行完毕if(done){return resolve(value)}else{//这个设计很巧妙,避免直接调用next()//使用Promise.resolve().then 调用,//使得不阻塞主线程的任意,而是把then里面//任务等其他主任务执行才调用return Promise.resolve(value).then(val=>step('next',val),err=>step("error",error))}}//调用第一个步骤step("next")})}
}
执行
asyncGenerator(testG)()
//
//data:  data
//data2:  data

在核心代码实现过程中,有一个地方设计很巧妙,如下:

  if(done){return resolve(value)}else{//这里不直接调用 next(), 而是使用 Promise.resolve(value).then 避免主线程阻塞return Promise.resolve(value).then(val=>step('next',val),err=>step("error",error))}

作者不直接调用 step('next',val),原因是因为如果直接调用next,那么主线程就会一直阻塞在这个函数内部,直到执行所有的generator步骤。
而是使用了Promise.resolve(value).then,将next 封装到then会调用函数,这个细节可以使得主线程执行完任务,再去任务队列执行next,实现主线程不阻塞。

关于Promise.resolve(value).then 的效果可以看以下代码
打印输出结果为1,3,2,很好解释上诉的作者为避免主线程阻塞的细节

console.log("1")
Promise.resolve("2").then(res=>console.log(res))
console.log("3")
//1
//3
//2

小结

  • generator 、promise 是ES6的新特性,await+async 是 ES7新加的,await +async 核心是可以使用generator+promise实现
  • generator 可以手动调用next 方法来执行下一步操作,节奏可以由开发者自己掌控
  • 出了使用next 方法迭代 generator ,还可以使用for of 迭代generator对象
  • 使用 generator实现 async+await效果,合理使用Promise.resolve(),可避免主线程阻塞

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

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

相关文章

C++内存分配策略

目录 基础概念 内存布局 分配方式 实现 1.new和delete 2.利用空间配置器alloc 3.用malloc和free 4.静态内存分配 基础概念 在讲内存分配之前,先对一些基础概念进行阐述,以便能更好的讨论问题 内存布局 代码编译为可执行程序后运行占用的内存可…

Java研学-过滤查询

一 概述 1 原理 根据用户传入的条件进行数据的筛选查询,最后将结果放回给用户,例如对员工的姓名与工资范围进行筛选查询 // 查询所有员工 SELECT * FROM employee // 模糊查询员工名称 SELECT * FROM employee WHERE name LIKE %黄% // 查询工资为8000…

欢迎关注我的公众号

后续我会在公众号更新相关的C/rust等相关语言知识,此外计算机基础相关的知识。 欢迎大家关注:松元漫话

了解统计分类中的贝叶斯理论误差限

一、介绍 统计分类和机器学习领域正在不断发展,努力提高预测模型的准确性和效率。这些进步的核心在于一个基本基准,即贝叶斯理论误差极限。这个概念深深植根于概率和统计学,是理解分类算法的局限性和潜力的基石。本文深入探讨了贝叶斯错误率的…

【LabVIEW FPGA入门】使用LabVIEW FPGA进行编程并进行编译

在本文中会进行一个简单的FPGA编程演示,这通常可以验证编译工具链是否正常使用。在LabVIEW FPGA中和rt、PC编程一样使用数据流编程,但是需要注意的是FPGA中有些函数是不可以用的,因为这些函数很占用资源,且FPGA只能同时下载运行一…

AI软件开发:探索原理、挑战与未来趋势

AI软件开发已经成为当前最热门和具有前景的技术领域之一。随着人工智能技术的快速发展,AI软件的应用范围也在不断扩大。本文将主要探讨AI软件开发的原理、挑战以及未来的趋势。 首先,AI软件开发的原理是基于机器学习和深度学习算法。机器学习是一种通过…

C#中Random.Next 方法

目录 一、重载 二、Next() 1.定义 2.示例 三、Next(Int32) 1.定义 2.示例1 3.示例2 四、Next(Int32, Int32) 1.定义 2.示例1 3.示例2 一、重载 返回一个随机整数。 Next()返回一个非负随机整数。Next(Int32)返回一个小于所指定最大值的非负随机整数。Next(Int32,…

Jetbrains ai assistant激活成功了

使用ai assistant插件助手 很完美,第一次用在idea 开发工具就完美的把激活了,你也不妨试试 链接地址:https://web.52shizhan.cn 激活后如下 登录页面 完美使用

Linux 文件和文本内容

本篇主要分两类,一类是vim 这种交互式的文本编辑软件,另外就是各种花式的文本处理命令,有 awk、sed 这种强大的命令,也有简单的重定向和echo打印,指在提升文本处理效率,建议试试,来增强记忆 一、…

python设计模式有哪几种

Python 中常见的设计模式有以下几种 一 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。 二 工厂模式(Factory Pattern):使用工厂方法来创建对象,而不是直…

c++算法之枚举

目录 解空间的类型 循环枚举解空间 例题 特别数的和 输入格式 输出格式 输入样例: 输出样例: 解 例题 反倍数 问题描述 输入格式 输出格式 样例输入 样例输出 解 例题 找到最多的数 解 枚举算法是一种基本的算法思想,它通过…

《设计模式的艺术》笔记 - 单例模式

介绍 单例模式优点是可以确保系统中只存在单个对象实例,缺点是不便扩展,一定程度上违背单一原则,既提供业务方法,又提供创建对象方法 饿汉式单例 在类加载的时候就创建好对象,获取对象时直接返回即可 class EagerSin…

喜报 | 联诚发斩获卓越品牌奖,总裁龙平芳荣获优秀企业家奖

1月10日,深圳市半导体产业发展促进会五届三次会员大会暨五届四次理/监事会在深圳宝安登喜路国际大酒店隆重举行,全体会员、党员、兄弟商协会等共600多位代表出席会议。联诚发LCF作为协会会长单位也受邀出席盛会。 本次大会颁布了卓越品牌奖、优秀企业奖、…

79LXX 三端负电源电压调节器,具有一系列固定电压输出,适用于小于100mA电源供给的场合

79LXX系列三端负电源电压调节器是单片双极型线性集成电路,采用TO92、SOT89-3的封装形式封装,有一系列固定的电压输出,适用于小于100mA电源供给的场合。 主要特点: 最大输出电流为100mA 固定输出电压分别为-5V、-6V、-8V、-9V、-1…

QT第三天

使用QT完成水果计价界面和功能&#xff0c;如下图&#xff1a; 运行结果&#xff1a; 代码&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QListWidgetItem>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_N…

redis复习总结

我的redis 1. redis集群 主从集群【哨兵集群】&#xff1a;主从集群是指中&#xff0c;存在一个master节点和多个slave节点。master节点负责接收客户端的读写&#xff0c;slave节点负责读操作。主节点一旦接收到数据的变更&#xff0c;就会将数据同步至slave节点。 但这样的…

用TF-IDF处理文本数据

计算机擅长处理数字&#xff0c;但不擅长处理文本数据&#xff0c;TF-IDF是处理文本数据最广泛使用的技术之一&#xff0c;本文对它的工作原理以及它的特性进行介绍。 根据直觉&#xff0c;我们认为在文本数据分析中出现频率更高的单词应该具有更大的权重&#xff0c;但事实并…

机器人技能学习-构建自己的数据集并进行训练

概要 若想训练自己的场景&#xff0c;数据集的重要性不做过多赘述&#xff0c;下面就基于 robomimic 和 robosuite 构建自己的数据集进行讲解&#xff0c;同时&#xff0c;也会附上 train 和 run 的流程&#xff0c;这样&#xff0c;就形成了闭环。 自建数据集 采集数据 采…

【MATLAB】REMD_LSTM神经网络时序预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 REMD-LSTM神经网络时序预测算法是一种结合了REMD&#xff08;Reservoir Enhanced Multi-scale Deep Learning&#xff09;算法和长短期记忆神经网络&#xff08;LSTM&#xff09;的时间序…

解决“Ubuntu系统与windows系统之间不能执行复制粘贴”之问题

在win11中&#xff0c;发现“Ubuntu系统与windows系统之间不能互相复制粘贴”&#xff0c;只能通过“FPT客户端FileZilla”才能交换文件&#xff0c;但遇到字符串&#xff0c;就没法实现了&#xff0c;因此&#xff0c;在两个系统之间实现互相复制和粘贴字符串&#xff0c;就很…