【ES6】Generator函数详解
【ES6】Generator函数详解
- 一、Generator函数简介
- 基本概念
- 函数写法
- yield关键字介绍
- 二、next方法的参数
- 三、for...of循环
- 四、关于普通throw()与Generator的throw()
- 五、Generator函数的应用【很重要】
- 1、延迟函数
- 2、简化函数的flag(Generator与状态机)
- 3、异步操作的同步化表达
- 4、函数的自动化控制【心生佩服】
- 查看更多ES6教学文章:
- 参考文献
引言:从Generator开始,才算是ES6相对高级的部分。之后的Promise、async都与异步编程有关。 |
一、Generator函数简介
先用最直白的话给大家介绍一下Generator函数:首先呢,Generator是一类函数,通过 * 号来定义。其次,Generator函数里特有的yield关键字,可以把函数里面的语句在执行时分步执行。用next()来执行。
例如,定义一函数:
function* test(){
yield console.log(“1”);
yield console.log(“2”);
yield console.log(“3”);
}
var t=test();t.next();t.next();t.next();
在执行第一个next的时候,输出1,第二个输出2,以此类推……这样,就把函数里面的内容分段执行了。
下面请看详细介绍。
基本概念
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。
对于Generator函数有多种理解角度。从语法上,首先可以把它理解成一个状态自动机,封装了多个内部状态。
执行Generator函数会返回一个遍历器对象。也就是说,Generator函数除了是状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
形式上,Generator 函数是一一个普通函数,但是有两个特征:一是function命令与函数名之间有一个星号;二是函数体内使用yield语句定义不同的内部状态。
/******** 代码块1-1 ********/
function* helloWorldGenerator() {yield 'hello';yield 'world';return 'ending';
}
var hw = helloWorldGenerator();
代码块1-1定义了一个Generator函数helloWorldGenerator, 它内部有两个yield语句"hello"和“world",即该函数有3个状态: hello、 world 和return语句(结束执行)。
Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一条yield语句(或return语句)为止。换言之,Generator 函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。
/******** 代码块1-2 ********/
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
上面的代码块1-2共调用了4次next方法。运行解释如下:
第1次调用,Generator 函数开始执行,直到遇到第一条yield语句为止。next方法返回一个对象,它的value属性就是当前yield语句的值hello,done属性的值false表示遍历还没有结束。
第2次调用,Generator 函数从上次yield语句停下的地方,一直执行到下一条yield语句。
第3次调用,Generator 函数从上次yield语句停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。next方法返回的对象的value属性就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true表示遍历已经结束。
第4次调用,此时Generator函数已经运行完毕,next方法返回的对象的value属性为undefined, done属性为true。以后再调用next方法,返回的都是这个值。
总结:调用Generator函数,返回一个遍历器对象,代表Generator函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值; done 属性是一个布尔值,表示是否遍历结束。
函数写法
ES6没有规定functon关键字与函数名之间的星号写在哪个位置。这导致下面代码块1-3的写法都能通过。
/******** 代码块1-3 ********/
function * foo(x, y) { ... }
function *foo(x, y) { ... }
function* foo(x, y) { ... }
function*foo(x, y) { ... }
yield关键字介绍
由于Generator函数返回的遍历器对象只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志。
遍历器对象的next方法的运行逻辑如下。
1、遇到yield语句就暂停执行后面的操作。并将紧跟在yield后的表达式的值作为返回的对象的value属性值。
2、下一次调用next方法时再继续往下执行,直到遇到下条yield语句。
3、如果没有再遇到新的yield语句,就一直运行到函数结束,直到returnr语句为止,并将return语句后面的表达式的值作为返回的对象的value属性值。
4、如果该函数没有return语句,则返回的对象的value属性值为undefined。
另外注意,yield语句不能用在普通函数中,否则会报错。
二、next方法的参数
yield语句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数, 该参数会被当作上一条yield语句的返回值。
/******** 代码块2-1 ********/function* foo(x) {var y=2 * (yield (x + 1));var z=yield(y/3);return(x+y+z);}var a = foo(5);a.next() // Object{value:6, done:false}a.next() // object{value:NaN, done:false}a.next() // object{value:NaN, done:false}var b = foo(5);b.next() // {value:6,done:false }b.next(12) // {value:8, done:false }b.next(13) // {value:42, done:true }
代码块2-1中,第二次运行next方法的时候不带参数,导致y的值等于2 * undefined(即NaN),除以3以后还是NaN, 因此返回对象的value属性也等于NaN。 第三次运行Next方法的时候不带参数,所以z等于undefined,返回对象的value属性等于5+NaN +undefined,即NaN。
如果向next方法提供参数,返回结果就完全不一样了。上面的代码第一次调用b的next方法时,返回x+1的值6;第二次调用next方法,将上一次yield语句的值设为12,因此y等于24,返回y / 3的值8;第三次调用next方法,将上一次yield语句的值设为13,因此z等于13, 这时x等于5,y等于24,所以return语句的值等于42。
三、for…of循环
for…of循环可以自动遍历Generator函数,且此时不再需要调用next方法。如代码块3-1。
/******** 代码块3-1 ********/function *foo() {yield 1;yield 2;yield 3;yield 4;yield 5;return 6;}for (let v of foo()) {console.log(v);}
//1 2 3 4 5
上面的代码使用for...of循环依次显示5条yield语句的值。这里需要注意,一旦next方法的返回对象的done属性为true,for...of循环就会终止,且不包含该返回对象,所以上面的return语句返回的6不包括在for...of循环中。
四、关于普通throw()与Generator的throw()
Generator函数返回的遍历器对象都有一个throw方法,可以在函数体外抛出错误,然后在Generator函数体内捕获。
我们知道在try...catch语句中,如果try语句中抛出了两个异常,当第一个异常抛出时,就会直接停止。
/******** 代码块4-1 ********/
var g = function* () {while (true) {try {yield;} catch (e) {if (e != 'a') throw e;console.log('内部捕获', e);}}
};var i = g();
i.next();try {i.throw('a');i.throw('b');} catch (e) {console.log('外部捕获',e);}
//内部捕获a
//外部捕获b
但是,上面的代码块4-1中,遍历器对象i连续抛出两个错误。第一个错误被Generator函数体内的catch语句捕获,然后Generator函数执行完成,于是第二个错误被函数体外的catch语句捕获。
注意,不要混淆遍历器对象的throw方法和全局的throw命令。上面的错误是用遍历器对象的throw方法抛出的,而不是用throw命令抛出的。后者只能被函数体外的catch语句捕获。
五、Generator函数的应用【很重要】
1、延迟函数
功能:对函数f()延迟2000ms后执行。见代码块5-1。
/******** 代码块5-1 ********/
function * f(){console.log('执行了');
}var g = f();setTimeout(function () {g.next();
},2000);
2、简化函数的flag(Generator与状态机)
功能:把一些需要flag的函数,去掉flag,大大简化函数体。见代码块5-2与5-3。
/******** 代码块5-2 原函数 ********/
var tickFlag = true;
var clock = function (){if(tickFlag)console.log('Tick');elseconsole.log('Tock');tickFlag=!tickFlag;
}
/******** 代码块5-3 简化后函数 ********/
var clock = function* (){while(true){yield console.log('Tick');yield console.log('Tock');}
}
3、异步操作的同步化表达
功能:假如现在有两个api分别是加载页面和卸载页面。普通写法见代码块5-4,同步化表达见代码块与5-5。
/******** 代码块5-4 原写法 ********///加载页面
showLoadingScreen();
//加载页面数据
loadUIDataAsynchronously();
//卸载页面
hideLoadingScreen();
/******** 代码块5-5 同步化后写法 ********/
function* loadUI(){showLoadingScreen();yield loadUIDataAsynchronously();hideLoadingScreen();
}var load = loadUI();
//加载UI
load.next();
//卸载UI
load.next();
其实,类似代码块5-5的写法,Vue里面有个概念Bus(中央总线),还有Java里面的线程的总线,都极为相似。感兴趣可以去查一查。
4、函数的自动化控制【心生佩服】
功能:如果有一个多步操作非常耗时,采用回调函数可能会很复杂。这时利用Generator函数可以改善代码运行的流程,类似于自动化控制。见代码块5-6。
/******** 代码块5-6 函数的自动化控制 ********/
function* longRunningTask() {try {var value1 = yield step1();var value2 = yield step2(value1);var value3 = yield step3(value2);var value4 = yield step4(value3);} catch (e) {// catch Error}
}scheduler(longRunningTask());//实现自动化控制function scheduler(task){setTimeout(function() {var taskObj = task.next(task.value);if(!taskObj.done){task.value = taskObj.value;}},0);
}
查看更多ES6教学文章:
1. 【ES6】let与const 详解
2. 【ES6】变量的解构赋值
3. 【ES6】字符串的拓展
4. 【ES6】正则表达式的拓展
5. 【ES6】数值的拓展
6. 【ES6】数组的拓展
7. 【ES6】函数的拓展
8. 【ES6】对象的拓展
9. 【ES6】JS第7种数据类型:Symbol
10. 【ES6】Proxy对象
11. 【ES6】JS的Set和Map数据结构
12. 【ES6】Generator函数详解
13. 【ES6】Promise对象详解
14. 【ES6】异步操作和async函数
15. 【ES6】JS类的用法class
16. 【ES6】Module模块详解
17. 【ES6】ES6编程规范 编程风格
参考文献
阮一峰 《ES6标准入门(第2版)》
【ES6】Generator函数详解相关推荐
- es6中的generator函数详解
Generator 函数的定义 语法上,Generator 函数是一个状态机,封装了多个内部状态. 形式上,Generator是一个函数.不同于普通函数,是可以暂停执行的,所以函数名之前要加星号,以示 ...
- generator函数详解
Generator 函数的定义 语法上,Generator 函数是一个状态机,封装了多个内部状态. 形式上,Generator是一个函数.不同于普通函数,是可以暂停执行的,所以函数名之前要加星号,以示 ...
- js中Generator函数详解
文章目录 1. Generator的定义和执行 2. Generator中yield在赋值号左边的情况 3. Generator函数嵌套使用 4. 使用generator函数完成网络请求 1. Gen ...
- Javascript 函数详解
Javascript 函数详解 1)函数声明: 通过关键字function定义,把函数作为变量来声明 函数声明后不会立即执行,会在我们需要的时候调用到. <script>function ...
- C++ - 随机数生成器(random-number generator) 的 详解 及 代码
随机数生成器(random-number generator) 的 详解 及 代码 本文地址: http://blog.csdn.net/caroline_wendy/article/details/ ...
- C语言网络编程:accept函数详解
文章目录 前言 函数描述 代码实例 如何得到客户端的IP 和 端口号 前言 当使用tcp服务器使用socket创建通信文件描述符,bind绑定了文件描述符,服务器ip和端口号,listen将服务器端的 ...
- 【FFmpeg】函数详解(三)
FFmpeg函数详解 14.av_write_frame 15.av_interleaved_write_frame 16.av_write_trailer 17.avio_close 18.av_i ...
- 【FFmpeg】函数详解(二)
FFmpeg函数详解 9.av_dump_format 10.avio_open 11.avformat_write_header 12.avcodec_send_frame 13.avcodec_r ...
- 【FFmpeg】函数详解(一)
FFmpeg函数详解 一.错误码相关 1.AVERROR 2.av_strerror 3.其他错误码解释 二.编解码 1.获取编解码器 2.申请.释放上下文环境 3.打开编码器avcodec_open ...
最新文章
- python抓取微博数据中心_有哪些「神奇」的数据获取方式?
- vim 忽略大小写查找
- 【LeetCode】字符串 string(共112题)
- 4到20ma模拟量转换公式_西门子 S7-1200 模拟量转换
- 岗位推荐 | 微软小冰团队招聘数据挖掘/算法工程师实习生
- Spark _28_窗口函数
- Docker Compose搭建consul群集环境(了解Docker Compose及常用命令,Docker四种网络,Doker指定端口)
- r语言regexpr函数_R语言学习笔记-文本挖掘之字符处理(1)
- Go语言中的单例模式
- DOM基础、定时器、BOM基础
- Vue 组件间的传值(通讯)
- 2016蓝桥杯C++A:网友年龄
- 大数据实训:实验二-基本SQL练习
- envi插件大津法_IDL处理Himawari8-NC数据
- android re浏览器下载,re浏览器官方版下载_re浏览器app下载4.9.6 - 系统城
- opencv中 画六边形
- 手机厂商要和年轻人交朋友,性价比日渐式微?
- 职业作秀V1.7.9更新公告
- 网络视频的防盗与破解
- js+css如何制作(音频)图标【切换播放动画】效果?
热门文章
- php中的全局变量$_POST收集表单数据
- statTarget-基于QC样本的代谢组学数据校正
- java 按顺序取出json_java取json 的方法
- DNA提取方法对浮游生物群落研究结果的影响
- MPB:农科院田健、韩东飞等-​​水稻根系互作功能微生物的筛选方法
- Nature子刊:涵盖20多万个基因组的人体肠道微生物参考基因组集
- MITRE:利用微生物组时间序列数据推断与宿主状态变化相关的特征
- Python使用matplotlib可视化树状图、层次聚类系统树图、树状图根据给定的距离度量将相似点分组在一起、并根据点的相似性将它们组织成树状图链接起来(Dendrogram)
- pandas使用query函数基于判断条件获得dataframe中满足条件的数据行(row)的索引列表(index of rows matching conditions in dataframe)
- R语言ggplot2可视化堆叠的条形图(stacked bar plot)并在每一个条形图的的中间添加对应的数值、值标签定位在geom_col堆叠的条形图中的每个条形段的中间