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 ...
随机推荐
- jsp传参 servlet接收中文乱码问题
在公司实习了8个月,一直都是做android和h5的,但是发现做程序连一点服务都不会该怎么办,所以最近开始学起了java,不知道是不是因为框架学多了,现在看起springmvc框架比以前看起来简单太多 ...
- 网站如何防止sql注入攻击的解决办法
首先我们来了解下什么是SQL注入,SQL注入简单来讲就是将一些非法参数插入到网站数据库中去,执行一些sql命令,比如查询数据库的账号密码,数据库的版本,数据库服务器的IP等等的一些操作,sql注入是目 ...
- latex02-LaTeX源文件的基本结构
1.一个latex文件有且仅有一个document环境 %后表示注释 2.latex的导言区用于全局设置. 例如:title\author\date 加入空行是结构更加清晰 为了能正确的使用标题信息, ...
- 640. Solve the Equation
class Solution { public: string solveEquation(string equation) { int idx = equation.find('='); , v1 ...
- (数据科学学习手札34)多层感知机原理详解&Python与R实现
一.简介 机器学习分为很多个领域,其中的连接主义指的就是以神经元(neuron)为基本结构的各式各样的神经网络,规范的定义是:由具有适应性的简单单元组成的广泛并行互连的网络,它的组织能够模拟生物神经系 ...
- shell重温---基础篇(函数操作)
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用.shell中函数的定义格式如下: [ function ] funname [()] { action; [ret ...
- hdu1233 继续畅通工程 (最小生成树——并查集)
还是畅通工程 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Sub ...
- struts2官方 中文教程 系列九:Debugging Struts
介绍 在Struts 2 web应用程序的开发过程中,您可能希望查看由Struts 2框架管理的信息.本教程将介绍两种工具,您可以使用它们来查看.一个工具是Struts 2的配置插件,另一个是调试拦截 ...
- 1826: [JSOI2010]缓存交换
1826: [JSOI2010]缓存交换 https://www.lydsy.com/JudgeOnline/problem.php?id=1826 分析: 简单的贪心,然后调啊调...最近怎么了,码 ...
- 「日常训练」 Mike and Fun (CFR305D2B)
题意(CodeForces 548B) 每次对01矩阵中的一位取反,问每次操作后,单列中最长连续1的长度. 分析 非常非常简单,但是我当时训练的时候WA了四次...无力吐槽了,人间 不值得.jpg 代 ...