【es6】中的Generator

Generator

  • 一、Generator 是什么?
    • 1.1 与普通函数写法不一样,有两个不同
  • 二、Generator 使用
    • 2.1 书写方法
  • 三、yield语句
    • 3.1 yield和return
    • 3.2 注意事项
    • 3.3 yield*语句
    • 3.4 yield*应用
  • 四、next方法
    • 4.1参数
    • 4.2 运行逻辑
  • 五、异步解决方案
  • 六、Generator相关面试题
    • 1. 对Iterator、Generator、Async/Await的理解?
    • 2. Generator 是怎么做到中断和恢复?
  • 总结


一、Generator 是什么?

Generator 函数是 ES6 提供的一种异步编程解决方案,最大特点交出函数的执行权。

和普通函数不一样的是必须调用next()才会执行函数。

1.1 与普通函数写法不一样,有两个不同

  • function与函数名之间有个(*)号;
    不同于普通函数,可暂停执行,所以加*区别。
  • Generator 函数体内部使用yield语句,可以定义不同的内部状态,其实就是数据不同。
    函数内部的状态,就是函数内部的值,它在不同的时候是不一样的。

本质上整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。
yield的命令是异步不同阶段的分界线,有时把yield的当成return。

二、Generator 使用

2.1 书写方法

  1. 函数名和方法中间有个 *号,但没规定放置的位置,一般跟在function后面;
  1. 调用Generator 函数,函数并不执行,返回的是一个指向内部状态指针的对象(遍历器对象)
function* test(){let n = 1;yield n;yield++n;yield--n;return n;
}let t = test();
console.log(t);  //test {<suspended>}

返回的是一个对象,并且有next, return,throw方法
在这里插入图片描述

  1. 想打印值,必须调用next方法,使指针下一个状态。也就是每次调用都从上一个指针状态(yield)开始,遇到下一个yield或return语句为止。也就是一段段段执行,yield暂停,next继续执行。

下面调用6次,一个yield一个状态。返回一个对象,value代表值,done代表是否遍历结束(true结束,false未结束)

done为true已经代表函数结束,再打印几次值都为undefined

console.log(t.next());  //{value: 1, done: false}
console.log(t.next());  //{value: 2, done: false}
console.log(t.next());  //{value: 1, done: false}
console.log(t.next());  //{value: 1, done: true}
console.log(t.next());  //{value: undefined, done: true}
console.log(t.next());  //{value: undefined, done: true}

三、yield语句

yield语句就是暂停标志。

3.1 yield和return

相同点:

  • 都能返回后面表达式的值。

不同点:

  • yield的函数暂停执行时,下一次会从该位置继续向后执行,并且一个函数可有多个yield语句。
  • return返回就相当于结束函数,一个函数只有一个return。

3.2 注意事项

  1. yield后面n+1不会执行,只有调用next方法才会执行。
function* test() {let n = 1;yield n + 1;console.log(n);
}
test();   //此时打印是空白的//必须调用next方法才会执行
console.log(test().next());  //{value: 2, done: false}

2.可以不用yield,但是必须调用next方法才会执行

function* test() {let n = 1;console.log(n);
}
let t = test();
console.log(t.next());
  1. yield语句不能用在普通函数中。
function test() {yield 1;
}
console.log(test());  // Unexpected number

3.3 yield*语句

Generator 函数内部调用另一个Generator 函数,默认情况无效。

function* A(){yield 1;yield 2;
}
function* B(){yield 3;A();yield 4;
}
for(let i of B()){console.log(i)
}
//3
//4

虽然在B函数中调用了A(),但只能打印出3,4。如果想要调用,就要在前面使用yield*语句。

function* A(){yield 1;yield 2;
}
function* B(){yield 3;yield* A();yield 4;
}
for(let i of B()){console.log(i)
}
//3
//1
//2
//4

yield*是for…of…的一种简写形式,我们知道for…of…可以遍历出具有iterator接口的数据结构(数组,类数组对象,字符串,nodelist等)

3.4 yield*应用

  1. 数组扁平化
let a = [1,[1,2],[1,2,3]]
function* iterTree(arr){if(Array.isArray(arr)){for(let i = 0;i<arr.length;i++){yield* iterTree(arr[i]);}}else{yield arr;}	
}
for(let i of iterTree(a)){console.log(i)
}
  1. 遍历完全二叉树
// 构建树
function Tree(left,middle,right){this.left = left;this.middle = middle;this.right = right;
}
// 中序遍历函数
function* inorder(t){if(t){yield* inorder(t.left);yield t.middle;yield* inorder(t.right);}
}
// 生成二叉树
function make(array){if(array.length == 1) return new Tree(null,array[0],null);return new Tree(make(array[0]),array[1],make(array[2]));
}
let tree = make([[['a'],'b',['c']],'d',[['e'],'f',['g']]]);
// 遍历二叉树
var result = [];
for(let node of inorder(tree)){result.push(node);
}
console.log(result)   //['a', 'b', 'c', 'd', 'e', 'f', 'g']

四、next方法

next方法是使指针指向下一个状态。除了for…of遍历出来,必须调用next才能打印出yield值。

4.1参数

yield语句本身没有返回值,或者总返回undefined。next方法可以带一个参数,这个参数会被当上一个yield语句的返回值。

function* test(x) {var a = yield x-1;var b = yield (a+2);return a+b;
}
let t = test(4);
console.log(t.next())     //{value: 3, done: false}
console.log(t.next(10))   //{value: 12, done: false}
console.log(t.next(4))    //{value: 14, done: true}

第一步:传个4进去,x-1=3,打印出来为3
第二步:传个10进去,这个参数会被当上一个yield语句的返回值,也就是a的值为10,a+2,打印出来为12。
第三步:传个4进去,上一个参数b的值为4,a的值为之前的10,a+b为14。

4.2 运行逻辑

  • 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
  • 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
    如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
  • 如果该函数没有return语句,则返回的对象的value属性值为undefined

五、异步解决方案

我们之前知道有以下几种异步解决方案:

  • 回调函数
    所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,再调用这个函数。
fs.readFile('/etc/fstab', function (err, data) {if (err) throw err;fs.readFile('/etc/shells', function (err, data) {if (err) throw err;console.log(data);});
});

readFile函数的第三个参数,就是回调函数,等到操作系统返回了/etc/fstab这个文件以后,回调函数才会执行。

  • Promise 对象
    Promise就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用。
const fs = require('fs');const readFile = function (fileName) {return new Promise(function (resolve, reject) {fs.readFile(fileName, function(error, data) {if (error) return reject(error);resolve(data);});});
};readFile('/etc/fstab').then(data =>{console.log(data)return readFile('/etc/shells')
}).then(data => {console.log(data)
})
  • async/await
const asyncReadFile = async function () {const f1 = await readFile('/etc/fstab');const f2 = await readFile('/etc/shells');console.log(f1.toString());console.log(f2.toString());
};
  • generator 函数
    yield表达式可以暂停函数执行,next方法用于恢复函数执行,这使得Generator函数非常适合将异步任务同步化。
const gen = function* () {const f1 = yield readFile('/etc/fstab');const f2 = yield readFile('/etc/shells');console.log(f1.toString());console.log(f2.toString());
};

区别:
通过上述代码进行分析,将promise、Generator、async/await进行比较:

  • promise和async/await是专门用于处理异步操作的

  • Generator并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator接口…)

  • promise编写代码相比Generator、async更为复杂化,且可读性也稍差

  • Generator、async需要与promise对象搭配处理异步情况

  • async实质是Generator的语法糖,相当于会自动执行Generator函数

  • async使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案

六、Generator相关面试题

1. 对Iterator、Generator、Async/Await的理解?

  • 1. Iterator

Iterator 是一个循环接口,任何实现了此接口的数据都可以被 for of 循环遍历;

我们常用的 for…of 循环,都是通过调用被循环对象的一个特殊函数 Iterator 来实现的,但是以前这个函数是隐藏的我们无法访问,从 Symbol 引入之后,我们就可以通过 Symbol.iterator 来直接读写这个特殊函数。

循环语句来说,他并不关心被循环的对象到底是什么,他只负责调用 data[Symbol.iterator] 函数,然后根据返回值来进行循环。

let obj = {}
obj[Symbol.iterator] = function() {let index = 1;return {next() {return {done: index > 5,value: index++}}}
}
for (var i of obj) {console.log(i,obj);
}

在这里插入图片描述

  • 2. Generator

Generator 可以看做是一个更加灵活的 Iterator ,他们之间是可以互相替代的,但是, Generator 由于可以通过 yield 随时暂停,因此可以很方便进行流程控制和状态管理,而 Iterator 就可能需要你写更多的代码进行相同的操作。

  • 3. Async/Await
    async是Generator 的一个语法糖;
    • async 对应的是 *
    • await 对应的是 yield
async function count () {let a = await 1;let b = await 2;return a+b
}

2. Generator 是怎么做到中断和恢复?

通过使用 yield 表达式来实现中断和恢复执行的功能。

当 Generator 函数被调用时,它并不会立即执行,而是返回一个迭代器对象。每次调用迭代器对象的 next() 方法时,Generator 函数会从上一次执行的位置继续执行,直到遇到下一个 yield 表达式或函数结束。此时,Generator 函数将返回一个包含当前值和执行状态的对象,其中 value 属性表示 yield 表达式的结果,done 属性表示是否执行完毕。


总结

Generator 函数是一种异步编程解决,可以更好的控制函数执行。其中yield是同步的,调用next()才能打印出值。

函数返回的是一个对象,而next方法返回的也是一个对象,其中有value(值),done(是否遍历完)。

一个next对应一个yield语句,当最后done为true时,再调用next方法,值都是undefined。全部遍历可使用for…of,其原理还是数据结构内部有iterator接口。

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

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

相关文章

Java“牵手”根据关键词搜索(分类搜索)lazada商品列表页面数据获取方法,lazadaAPI实现批量商品数据抓取示例

lazada商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取lazada商品列表和商品详情页面数据&#xff0c;您可以通过开放平台的接口或者直接访问lazada商城的网页来获取商品详情信息。以下是两种常用方法的介…

量子非凡暴风去广告接口

>>>https://videos.centos.chat/lzffbf.php/?url 免费提供综合去广告接口&#xff0c;各位请友好调用

Android 使用模拟器模拟Linux操作系统

1. 简介 在Android手机上使用模拟器模拟ubuntu等操作系统&#xff0c;便于测试 2. 软件准备 Termux&#xff1a;是一款 Android 终端模拟器和 Linux 环境应用程序&#xff0c;无需 root 或设置即可直接运行。虽然酷安和谷歌菜市场都能下载&#xff0c;但这些渠道都很久没更新…

集成学习:Bagging, Boosting,Stacking

目录 集成学习 一、bagging 二、boosting Bagging VS Boosting 1.1 集成学习是什么&#xff1f; Bagging Boosting Stacking 总结 集成学习 好比人做出一个决策时&#xff0c;会从不同方面&#xff0c;不同角度&#xff0c;不同层次去思考&#xff08;多个自我&am…

保姆级别0-10级完整的在线企业帮助中心、官网博客搭建教程及工具

在今天的数字时代&#xff0c;拥有一个完善的在线企业帮助中心和官网博客是非常重要的。这些平台不仅可以提供有关产品和服务的信息&#xff0c;还可以增加客户互动&#xff0c;建立品牌声誉。在这个教程中&#xff0c;我们将从零开始创建一个帮助中心和官网博客&#xff0c;包…

Config:服务端连接Git配置

创建子模块 Pom文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org…

飞天使-k8s基础组件分析-安全

文章目录 名称空间解释访问kubernetes API的控制RBAC的介绍 kubeconfig用户的创建集群默认角色 给组创建授权针对pod配置服务账户参考文档 名称空间解释 名字是啥&#xff1f; 答&#xff1a;集群中每个对象的名称对于该类型的资源都是唯一的。并且每一个对象在整个集群中也有…

微服务基础知识

文章目录 微服务基础知识一、系统架构的演变1、单体应用架构2、垂直应用架构3、分布式SOA架构&#xff08;1&#xff09;什么是SOA&#xff08;2&#xff09;SOA架构 4、微服务架构5、SOA和微服务的关系&#xff08;1&#xff09;SOA&#xff08;2&#xff09;微服务架构 二、分…

【QT】progressBar的使用(13)

progressBar多用于记录程序运行的时间、文件下载的时间等等&#xff0c;今天就来看一下&#xff0c;如何熟练运用progressBar。 一.环境配置 1.python 3.7.8 可直接进入官网下载安装&#xff1a;Download Python | Python.org 2.QT Designer 官方下载路径&#xff1a;Qt…

数据处理 | Python实现基于DFCP张量分解结合贝叶斯优化的缺失数据填补

数据处理 | Python实现基于DFCP张量分解结合贝叶斯优化的缺失数据填补 目录 数据处理 | Python实现基于DFCP张量分解结合贝叶斯优化的缺失数据填补实践过程基本介绍研究背景程序设计参考资料实践过程 基本介绍 数据处理 | Python实现基于DFCP张量分解结合贝叶斯优化的缺失数据填…

【LeetCode-中等题】48. 旋转图像

文章目录 题目方法一&#xff1a;使用辅助数组矩阵 行列的规律方法二&#xff1a;原地修改 递推公式 题目 方法一&#xff1a;使用辅助数组矩阵 行列的规律 public void rotate(int[][] matrix) {int n matrix.length;int[][] matrix_new new int[n][n];for(int i 0 ; i<…

深入浅出AXI协议(2)——通道及信号

一、前言 在之前的文章中&#xff0c;我们主要介绍了什么是AXI协议&#xff0c;AXI协议的特点与优点&#xff0c;然后对于AXI协议非常重要的五通道结构进行了介绍&#xff0c;了解了5个通道各自的作用。本文我们继续AXI协议的学习&#xff0c;我们将讨论5个通道的具体内容和相对…

uni、js——点击与禁用(不可点击)、动态样式class

案例 没约满的时间可以点击进行选择&#xff0c;约满的就不能选择了。选择完之后变色变字。 核心思想就是创建一个第三方变量存起来&#xff0c;点击谁就存到第三方&#xff0c;在根据这个进行判断。 代码 <template><view class"content"><view cl…

JavaScript:基本语法(变量与函数的定义与使用)

文章目录 script 标签srcdefer 延迟加载 基本语法定义变量 与 使用变量基本类型typeof 查看变量类型复合类型数组类型定义对象类型定义 函数定义函数使用函数 script 标签 src 和scc一样可以内嵌也可以外src外引。 一般是推荐外引。 <script src"idx.js">&l…

opencv 进阶15-检测DoG特征并提取SIFT描述符cv2.SIFT_create()

前面我们已经了解了Harris函数来进行角点检测&#xff0c;因为角点的特性&#xff0c;这些角点在图像旋转的时候也可以被检测到。但是&#xff0c;如果我们放大或缩小图像时&#xff0c;就可能会丢失图像的某些部分&#xff0c;甚至有可能增加角点的质量。这种损失的现象需要一…

ant design自定义展开折叠查看子项和点击行查看详情

实现思路&#xff1a;通过配置rowSelection&#xff0c;列表项是否可选择来实现。 页面内容&#xff1a; <a-table :dataSource"integrationBonds" :columns"columns" :customRow"customintegrationBondsRow":pagination"{hideOnSingle…

Docker创建Consul并添加权限控制

一、部署Consul 1、拉取镜像&#xff1a; docker pull consul:<consul-version> 2、运行 docker run --name consul1 -p 8300:8300/tcp -p 8301:8301/tcp -p 8301:8301/udp -p 8302:8302/tcp -p 8302:8302/udp -p 8500:8500 -p 8600:8600/tcp -p 8600:8600/udp -v /h…

用Cmake build OpenCV后,在VS中查看OpenCV源码的方法(环境VS2022+openCV4.8.0) Part I

用Cmake build OpenCV后&#xff0c;在VS中查看OpenCV源码的方法 Part I 本文打算分成两部分写&#xff1a; 第一部分力求用最简单的办法帮助大家直接在VS中查看OpenCV的源码。这种方法最大的优点就是不容易出错&#xff0c;且操作简单。如果只是需要查看OpenCV源码的同学&…

matlab使用教程(24)—常微分方程(ODE)求解器

1.常微分方程 常微分方程 (ODE) 包含与一个自变量 t&#xff08;通常称为时间&#xff09;相关的因变量 y 的一个或多个导数。此处用于表示 y 关于 t 的导数的表示法对于一阶导数为 y ′ &#xff0c;对于二阶导数为 y ′′&#xff0c;依此类推。ODE 的阶数等于 y 在方程中…

Springboot配置高级

临时属性设置 带属性数启动SpringBoot java –jar springboot.jar –-server.port80携带多个属性启动SpringBoot&#xff0c;属性间使用空格分隔 属性加载优先顺序 参看https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-fea…