generator是什么?

generator是ES6提供的一种异步编程解决方案,在语法上,可以把它理解为一个状态机,内部封装了多种状态。执行generator,会生成返回一个遍历器对象。返回的遍历器对象,可以依次遍历generator函数的每一个状态。同时ES6规定这个遍历器是Generator函数的实例,也继承了Genarator函数的prototype对象上的方法。

最简单的generator函数,其实它就是一个普通的函数,但是它有两个特征。第一就是function关键字与函数名之间有一个*号,其二就是函数体内使用yield表达式来遍历状态:

function* newGenerator(){
yield 'hello';
yield 'world';
return 'ending';
}

执行generator函数之后,该函数并不会立即执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。通常使用遍历器对象的next方法。使得指针移向下一个状态。每一次调用next()方法,内部指针就从函数头部或上一次停下里的地方开始执行,直到遇到下一个yield表达式位置,由此可以看出,generator是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

generator中的yield表达式

yield表达式在generator中是作为一个暂停标志,当碰到yield时,函数暂停执行,等到下一次next()执行时,函数才从当前yield位置开始执行。并且,yield表达式只能用在Generator函数里边;同时,yield如果后边带一个,则就是相当于一个for...of的简写形式,如果yield后边不带,则返回的是generator的值。

function* gen() {
yield 'hello';
yield* 'hello';
}
let f = gen();
console.log(f.next().value);
console.log(f.next().value);
console.log(f.next().value);
console.log(f.next().value);
console.log(f.next().value);

上述例子中的后四个next()函数,就会顺序的返回h e l l

generator中的next函数

通过next函数,可以执行对应的yield表达式,且next()函数还可以带参数,该参数可以作为上一次yield表达式的返回值,因为yield本身是没有返回值的,如果next()中不带参数,则yield每次运行之后的返回值都是为undefined;

function* dataConsumer() {
console.log('Started');
console.log(`1. ${yield}`);
console.log(`2. ${yield}`);
return 'result';
} let genObj = dataConsumer();
genObj.next();
// Started
genObj.next('a');
// 1. a
genObj.next('b');
// 2. b

上述函数中,第一次运行next(),运行到第一个next()函数截止,第二个next运行时,传入的参数为'a';则运行到第二个yield地方截止,然后第一个yield运行的返回值为'a',依次类推,则得到上述结果。

另外,通过for...of可以循环generator中的所有状态,并且不需要使用next()函数。除了for...of循环以外,扩展运算符(...),解构赋值和Array.form方法内部调用的,都是遍历器接口。

generator生成的对象,还有其他一些函数,比如throw()用来抛出错误,return()用来定义返回值并终止generator的状态。

以上的三个方法在本质上其实是一样的,他们就是让generator恢复执行,并且使用不同的语句来替代yield语句。

  • next()是将yield表达式替换成一个值
  • throw()是将yield表达式替换成一个throw语句
  • return()是将yield表达式替换成一个return语句

Generator与协程

协程可以理解为“协作的线程”或者“协作的函数”。协程既可以是单线程实现,也可以用多线程实现,前者是一种特殊的子例程,后者是一种特殊的线程。

协程有点像函数,又有点像线程,它的运行流程大致如下。

  • 第一步,协程A开始执行
  • 第二部,协程A执行到一半,进入暂停,执行权转移到协程B
  • 第三步,(过了一段时间)协程B交换执行权
  • 最后,协程A恢复执行

协程适合用于多任务运行环境,它与普通的线程很相似,都有自己的执行上下文,可以分享全局量。他们的不同之处在于,同一时间可以有多个线程处于运行状态,但是运行的协程只能有一个,其他协程都是处于暂停状态。

由于JavaScript是单线程,只能保持一个调用栈,引入协程之后,每一个任务可以保持自己的调用栈,这样就可以再抛出错误的时候找到原始的调用栈,不至于像异步操作的回调函数那样,一旦出错,原始的调用栈早就结束了。

Generator 函数是 ES6 对协程的实现,但属于不完全实现。Generator函数被称为“半协程”(semi-coroutine),意思是只有 Generator 函数的调用者,才能将程序的执行权还给 Generator函数。如果是完全执行的协程,任何函数都可以让暂停的协程继续执行。

如果将Generator函数当做协程,完全可以将多个需要互相协作的任务写成Generator函数,他们之间使用yield标识交换控制权。

Generator 函数执行产生的上下文环境,一旦遇到yield命令,就会暂时退出堆栈,但是并不消失,里面的所有变量和对象会冻结在当前状态。等到对它执行next命令时,这个上下文环境又会重新加入调用栈,冻结的变量和对象恢复执行。

Generator函数的多种用途

  • 可以使异步操作来实现同步化表达
  • 控制流管理
  • 部署Iterator接口
  • 做为数据结构

上述的介绍中,我们看到了generator是什么,下边我们看一下,目前中,我们使用最多的,generator函数的异步调用。

异步编程对于单线程的JavaScript无疑是十分重要的(可以笼统的将异步定义为不连续的执行)。之前的文章中,我们也说过,JavaScript中对于异步的实现,就是回调函数。我们之前也有使用过promise去进行死亡回调的改良,promise来使回调嵌套的表现形式更好了些。其实呢generator函数也可以用来实现异步回调的嵌套。

整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。即为next方法还可以接收参数,向Generator函数体内输入数据。

Thunk函数

Thunk函数是自动执行Generator函数的一种方法。

对于以前的函数参数的求值计算,有两种计算方式,一种是传值调用,一种是传名调用。编辑器中的“传名调用”的实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体,这个临时函数就叫做Thunk函数。

JavaScript中使用的是传值调用,它的Thunk函数含义有所不同。再JavaScript中,替换的不是表达式,而是多参数函数的休整。

Thunk函数可以用于Generator函数的自动流程管理。即使Generator函数可以自动执行。

co模块

co模块用于Generator函数的自动执行。co函数返回一个Promise对象,因此可以用then方法添加回调函数。

使用了Generator函数之后,我们可以将多个异步嵌套的代码改为同步写异步,大大的简化了我们的代码量,以及代码的美观。

上边就是大概的ES中的Generator函数的介绍,我们可以使用Generator来实现 lazy evaluation,Iterator和实现异步调用同步的写法,但是其中我们面临的更多的还是generator的执行问题,下一篇我们来看一下generator是怎么实现async/await来控制异步。以及co模块的实现。

ES6的generator函数的更多相关文章

  1. ES6 学习 -- Generator函数

    (1)语法说明:Generator函数其实是一个普通函数,其有两个特点,一是,function关键字与函数名之间有一个星号(*):二是Generator函数内部使用yield表达式,定义不同的状态,然 ...

  2. 【es6】Generator 函数

    1. 基本概念 状态机,封装了多个内部状态 2. 应用 返回一个遍历器对象. 3. 代码形式 function* helloWorldGenertor() { yield 'hello'; yield ...

  3. ES6 - Note7:Generator函数

    Generator函数 1.Generator函数是ES6增加的异步编程解决方案之一,与普通的函数行为完全不同,类似于一个状态机,内部封装了多个状态. 在函数定义的形式上,跟普通函数差不多,有两处不同 ...

  4. ES6入门之Generator函数

    Generator Generator函数是ES6提供的一种异步编程解决方案,Generator函数是一个状态机,封装了多个内部状态. 执行Generator函数会返回一个遍历器对象,也就是说,Gen ...

  5. 转: ES6异步编程:Generator 函数的含义与用法

    转: ES6异步编程:Generator 函数的含义与用法 异步编程对 JavaScript 语言太重要.JavaScript 只有一根线程,如果没有异步编程,根本没法用,非卡死不可. 以前,异步编程 ...

  6. es6 generator函数

    es6 新增了Generator函数,一种异步编程的解决方案 回顾一下,es6 提供了新的遍历方法,for of ,适用于各种数据集合,统一了遍历操作,原生支持for of 集合的数据集合有.数组,字 ...

  7. ES6必知必会 (七)—— Generator 函数

    Generator 函数 1.Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同,通常有两个特征: function关键字与函数名之间有一个星号: 函数体内部使 ...

  8. ES6的新特性(17)——Generator 函数的异步应用

    Generator 函数的异步应用 异步编程对 JavaScript 语言太重要.Javascript 语言的执行环境是“单线程”的,如果没有异步编程,根本没法用,非卡死不可.本章主要介绍 Gener ...

  9. ES6的新特性(16)——Generator 函数的语法

    Generator 函数的语法 简介 基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同.本章详细介绍 Generator 函数的语法和 API,它的 ...

随机推荐

  1. session不会过期

    $(function () { window.setInterval(function () { $.post('random.html'); }, 60000); }); 加在母版页里,使用与长时间 ...

  2. DQL、DML、DDL、DCL

    二. SQL语言的分类 SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL. 1. 数据查询语言DQL数据查询语言DQL基本结构是由SELECT子句 ...

  3. pyinstaller打包py文件成exe文件时,出现ImportError: No module named 'pefile'错误解决办法!

    首先pyinstaller的安装与使用详见如下链接: 安装完成之后,命令行中输入pyinstaller之后,结果如下: ImportError: No module named 'pefile' 缺少 ...

  4. Java Web项目(Extjs)报错三

    1. Java Web项目(Extjs)报错三 具体报错如下: at org.jbpm.pvm.internal.processengine.SpringHelper.createProcessEng ...

  5. freemarker自定义标签报错(一)

    freemarker自定义标签 1.错误描述 freemarker.core.ParseException: Token manager error: freemarker.core.TokenMgr ...

  6. 原生态的ajax代码

    <script type="text/javascript"> var xmlhttprequest; function GetXmlHttpRequest() { i ...

  7. Linux之shell典型应用之脚本实现

    一. 提取Linux操作系统信息 二. 获取操作系统运行状态 三. 分析应用状态 四. 应用日志分析 第一章:VIM编辑器设置 一.语法高亮     syntax on/off 二.显示行号 set ...

  8. mongodb在windows下安装启动

    mongodb安装 mongodb配置 创建几个文件夹具体如下:数据库路径(\data\db\目录).日志路径(\data\log\目录)和日志文件(mongod.log文件)c:\data\log\ ...

  9. Mybatis常见面试题

    Mybatis常见面试题 #{}和${}的区别是什么? #{}和${}的区别是什么? 在Mybatis中,有两种占位符 #{}解析传递进来的参数数据 ${}对传递进来的参数原样拼接在SQL中 #{}是 ...

  10. [HNOI2015]亚瑟王

    题面在这里 题意 \(n\)张卡按照一定顺序排列,每轮从第\(1\)张开始考虑到最后一张,考虑一张卡时有\(p[i]\)的概率产生\(d[i]\)的贡献,产生贡献时直接退出该轮并在之后的考虑中直接跳过 ...