Generator实质
Generator实质
ES6里面最有意思,也是最有用的除了Promise之外就是Generator
了,关于Generator
的规范也是看了有一段时间了,今天想起来还是写一写这部分的内容。
用一句简单的话来概括Generator
的核心技术的话,那就是:将EC保存起来,每次执行代码的时候恢复EC,这样一个函数里面的代码就可以一小段一小段的去执行了。而且作用域链也会被保存起来了,所以JS原有的闭包和词法作用域也是依旧不变的。
具体我们通过ES6规范来看一看其中奥妙吧。
Generator
generator函数执行的时候,会进行如下动作:
- 创建一个VO,与当前EC(Execution Context,以下简称EC)的作用域链组成新的作用域链
- 创建一个generator对象,其有如下值:
- Scope:新建的作用域链
- Code:generator function内部的代码
- ExecutionContext:EC,目前值为null
- State:”newborn”
- Handler:默认的generator的处理器
这里可以看到,Generator函数的执行,函数体内部的代码是不会动的,而是创建一个generator对象,将代码存入其中,并给予相关的上下文
yield的行为
当执行到yield e时:
- 计算出表达式e的值
- 获取当前的EC,并从中获取currentGenerator,也就是yield所在的generator对象
- 使这个generator对象的ExecutionContext指向当前EC,并将其state修改为suspended
- 从EC栈弹出当前的EC
- 返回(normal, 1中的结果值, null)
可以看到,yield本身会先获得表达式的值后,将EC从栈顶弹出,交予generator对象。最后会返回一个结构,其含有三个属性,分别为运行结果、计算的结果值和null,Resume在检测到这个结构后,将停止代码的运行
这里yield之后将会返回到当前函数之外,作用域将发生改变,EC栈中的栈顶也会随之改变。而我们在generator function的函数体内部的这个EC,在下一次回来继续执行时依旧需要使用,所以这里就要交给generator对象代为管理一下,等下次回来,将重新压入EC栈的栈顶
return的行为
当执行到return e时:
- 计算出表达式e的值
- 获取当前EC,并从中获取currentGenerator,也就是return所在的generator对象
- 将这个generator对象的状态修改为closed
- 创建一个class为StopIteration的新对象,并使其value属性为1中计算的结果值
- throw这个对象
return也是一样,它同样需要先计算出表达式的值。但之后它获得了generator对象并不是为了做EC栈的维护,而是为了修改generator对象的状态
私有属性
- prototype:Object.prototype
- code:generator函数的函数体
- ExecutionContext:内部代码运行使用的EC
- Scope:作用域链
- Handler:标准的generator句柄
- State:newborn、executing、suspended、closed
- Send:看内部方法部分
- Throw:看内部方法部分
- Close:看内部方法部分
外部接口
next
- 如果this指向的不是generator对象,抛异常
- 调用this.send,传入一个undefined
- 返回结果
调用私有send方法
send
send方法允许指定一个值,作为上一次yield的返回值
- 如果this指向的不是generator对象,抛异常
- 调用this.send,传入当前第一个参数
- 返回结果
同样是调用私有send方法,不过传入了参数
throw
- 如果this指向的不是generator对象,抛异常
- 调用this.throw,传入当前第一个参数
- 返回结果
close
调用close方法可以直接以当前的value作为Generator的返回值
- 如果this指向的不是generator对象,抛异常
- 调用this.close,不传入任何参数
- 返回结果
iterate
由于每个generator对象都是一个iterator对象,直接return this就可以了
小结
接口都是内部方法的一层封装,可以看到next和send实际上都是send内部方法的包装
状态定义
- newborn:Code不为null,EC为null
- executing:Code为null,EC不为null,且generator对象的EC为当前EC
- suspended:Code为null,EC不为null,且generator对象的EC不为当前EC
- closed:Code为null,EC为null
调用了generator function后,生成的generator对象状态即为newborn。也就表明当前generator对象刚刚新建,还没有运行里面的任何代码。同时可以看到EC为null,说明内部运行时的EC并不存在
调用了send方法后,状态会修改为executing,send方法会使用Resume去执行代码,直到遇到yield或者return。遇到yield后,代码停止继续执行,状态修改为suspended,等待下次send。遇到return后,状态将被修改为closed,说明执行完毕。
当然也可以通过close方法,手动修改状态为closed
内部方法
send方法
- 判断generator对象的state,如果是executing或者closed,就报错。已经在运行了不能重复运行,已经关闭的自然不能运行
- 如果state为newborn
- 将判断传入的参数是否为undefined(外部接口next传入undefined,send则传入给的参数)。这里如果不是undefined,就报错。也就是说刚创建的generator对象不能调用含有参数的send外部接口。
- 创建一个新的EC,这个新的EC的currentGenerator执行这个generator对象,其作用域链为这个generator对象的作用域链
- 将这个EC压入EC栈中
- 执行generator中的代码,并返回或得到的结果
- 能到这,说明state只能是suspended。将state修改为executing,通过Resume(generator的ExecutionContext, normal, 传入的参数)获取结果并返回
generator对象的next和send方法的真正实现,其只处理newborn和suspended状态
在newborn状态下,这个generator内部的代码还没有被执行,其内部代码执行时的EC也没有被创建。所以需要创建一个EC并压入EC栈中
而state为suspended就没有这个EC初始化的过程了,内部代码执行时的EC已经在generator的ExecutionContext上了,所以只要修改状态为executing,然后使用Resume执行代码就好
throw
- 获取generator对象的state,如果为executing或者closed,无法抛异常,报错
- 如果state为newborn,那么state修改为closed,code修改为null,返回一个包含传入参数的异常
- 到这里说明state为suspended,修改state为executing,然后通过Resume(generator.ExectionContext, throw, 传入的参数)获得结果,并返回
- 这里如果是suspended,那么需要通过Resume,且completionType为throw来进行抛错
close
- 获取generator对象的state,如果state为executing,那说明代码正在运行,为了防止出现错误,禁止close。
- 如果state已经是closed了,那直接return就好
- 如果state为newborn,state修改为closed,code修改为null,然后返回(normal, undefined, null)
- 如果state为suspended,将其修改为executing,通过Resume(generator.ExecutionContext, return, undefined)获得结果,然后修改状态为closed,返回Resume获得的结果
调用close方法可以直接以当前的value作为Generator的返回值,当为newborn时,还没有value,自然是undeinfed。而如果是suspended,就有value了,那么就需要通过Resume,且completionType为return来立即返回
Resume(EC, completionType, V)
- 将这个传入的EC(generator的ExecutionContext)压入到EC栈中
- 从EC通过currentGenerator获取单签generator对象
- 设置当前作用域链为当前generator对象的作用域链
- 继续执行代码,并根据completionType做相应的处理
参考
Generator实质的更多相关文章
- Generator函数异步应用
转载请注明出处: Generator函数异步应用 上一篇文章详细的介绍了Generator函数的语法,这篇文章来说一下如何使用Generator函数来实现异步编程. 或许用Generator函数来实现 ...
- JavaScript中的Generator函数
1. 简介 Generator函数时ES6提供的一种异步编程解决方案.Generator语法行为和普通函数完全不同,我们可以把Generator理解为一个包含了多个内部状态的状态机. 执行Genera ...
- async/await 与 generator、co 的对比
之前写过一个分批预加载资源的插件,其实质便是串行执行异步,使用的方法是generator + promise -- 前几天写了一个爬虫,抓取页面的n个页面的音频资源,其也是串行执行异步,但是在使用的a ...
- 15.Generator 函数的语法
Generator 函数的语法 Generator 函数的语法 简介 基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同.本章详细介绍 Generat ...
- ES6的新特性(16)——Generator 函数的语法
Generator 函数的语法 简介 基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同.本章详细介绍 Generator 函数的语法和 API,它的 ...
- Generator 函数的语法
简介 § ⇧ 基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同.本章详细介绍 Generator 函数的语法和 API,它的异步编程应用请看< ...
- Python的程序结构[7] -> 生成器/Generator -> 生成器浅析
生成器 / Generator 目录 关于生成器 生成器与迭代器 生成器的建立 通过迭代生成器获取值 生成器的 close 方法 生成器的 send 方法 生成器的 throw 方法 空生成器的检测方 ...
- 浅谈Generator和Promise原理及实现
Generator 熟悉ES6语法的同学们肯定对Generator(生成器)函数不陌生,这是一个化异步为同步的利器. 栗子: function* abc() { let count = 0; whil ...
- TypeScript 迭代器(iterator)和生成器(generator)
⒈迭代器(iterator) 1.可迭代性 当一个对象实现了Symbol.iterator属性时,我们认为它是可迭代的. 一些内置的类型如 Array,Map,Set,String,Int32Arra ...
随机推荐
- rhel7-NFS服务搭建
检查服务: [root@localhost ~]# systemctl status nfs● nfs-server.service - NFS server and services Loade ...
- webug学习(1)
webug的题目,比较简单,拿来巩固一哈. 1. 一看就知道是注入漏洞了,啥也不说sqlmap直接开炮. 先-u 之后-u 网址 --current-db 获取当前网址的数据库 所以当前数据库就是 p ...
- GDOI DAY1游记
今天,是本蒟蒻的第一次参加GDOI,真激动! 今天,是GDOI第一天,昨天熬夜打代码,今天早上状态十分不好,于是... 进入了考场,叫我们自由打一会代码,于是...打了一坨AC机,重要的是错了(额.. ...
- Create Fiori List App Report with ABAP CDS view – PART 2
In the Part 1 blog, we have discussed below topics CDS annotations for Fiori List Report. How to cre ...
- 6.JAVA知识点归纳整理
一.jdk初识与HelloWord: 二.java基础: 2.1 标识符_关键字_数据类型 2.2 数据类型转换 2.3 程序编写格式 2.4 运算符 2.5 分支与for循环 2.6 while_b ...
- golang 小知识点记录
获取url中的参数及输出到页面的几种方式 func SayHello(w http.ResponseWriter, req *http.Request) { req.Method //获取url的方法 ...
- 安装Sql Server 2008的时候报错说找不到某个安装文件
在安装Sql Server 2008的时候,报错说找不到某个安装文件,但是这个文件明明在那,百思不得其解. 最后看到一个老外的文章里面说,你要确认,你能访问到这个文件 ...
- Percona-Tookit工具包之pt-mysql-summary
Preface Sometimes we need to collect information of MySQL server as a report when we first ...
- 基于阿里云服务器Linux系统部署JavaWeb项目
前段时间刚完成一个JavaWeb项目,想着怎么部署到服务器上,边学边做,花了点时间终于成功部署了,这里总结记录一下过程中所遇到的问题及解决方法.之所以选择阿里云,考虑到它是使用用户最多也是最广泛的云服 ...
- Prometheus 普罗米修斯监控
周末在家无聊 看新闻 看到关于监控的东西 拿来玩玩 试一下 感觉还蛮有意思 特此记录一下 这里只介绍客户端的配置 1:首先在POM中添加依赖 <dependency> <groupI ...