ES6 - Note7:Generator函数
Generator函数
1.Generator函数是ES6增加的异步编程解决方案之一,与普通的函数行为完全不同,类似于一个状态机,内部封装了多个状态。
在函数定义的形式上,跟普通函数差不多,有两处不同,一是function关键字与函数名之间需要一个星号(*),二是函数内部使用yield语句定义各种状态,且yield只能用在Generator函数中,否则报错,如下所示
function* testGenerator(){//星号只要在function与函数名之间就可
yield 'test';
yield 'generator';
return '!';
}
function testYield(){
yield 'hello';//报错,但在ff浏览器中不会报错,被自动认为是Generator函数
}
VM1211:9 Uncaught SyntaxError: Unexpected string(…)
调用Generator函数,该函数不会立即执行,而是返回一个遍历器Iterator,必须调用该遍历器的next方法去遍历函数内部的下一个状态,如下所示
function* generator(){
console.log('hehe');
yield 'hello';
yield 'ecmascript';
return 'end';
}
var gen = generator();
gen.next();
hehe
Object { value: "hello", done: false }
gen.next()
Object { value: "ecmascript", done: false }
gen.next()
Object { value: "end", done: true }
gen.next()
Object { value: undefined, done: true }
当然也可以使用for..of或者扩展运算符遍历,但不会遍历到return返回值,如下所示
for(let x of generator()){
console.log(x);
}
hehe
hello
ecmascript
[...generator()].forEach((val,idx,arr)=>console.log(val));
hehe
hello
ecmascript
Generator函数中yield语句是暂停标志,可以不存在该语句,这时函数可以当作是暂缓执行函数,如下所示
function* genfunc(){
console.log("稍后执行...");
}
var g = genfunc();
setTimeout(() => g.next(),2000);
3
稍后执行...
由于yield语句只能用在Generator函数中,因此在使用回调函数时需要特别的注意,比如在Generator函数使用数组的map,forEach方法时,不能在函数参数里面写yield语句,如下所示
function* arrGene(arr){
arr.forEach(function(val,idx,arr){
yield val;
});
}
console.log([...arrGene([1,2,3])]);
VM142:4 Uncaught SyntaxError: Unexpected identifier(…)
-------------------------使用for循环代替--------------------------
function* arrGene(arr){
for(let i=0,len=arr.length; i<len; i++){
yield arr[i];
}
}
console.log([...arrGene([1,2,3])]);
VM179:8 [1, 2, 3]
Generator是一个遍历器生成器,因此可以赋值给没有默认遍历器的对象的Symbol.iterator属性,让该对象能够使用for...of语句,如下所示
var obj = {};
obj[Symbol.iterator] = function* (){
yield 'hello';
yield 'world';
return '!';
}
for(let x of obj){
console.log(x);
}
hello
world
2.next方法参数
Generator实例的next方法可以传递参数,作为该实例内部上一个yield语句的返回值,如不通过next方法传值,yield语句的返回值总是undefined,如下所示
//不传值的情况
function* generator(){
console.log('hello generator...');
let v = yield 'ni';
let u = yield v+'test';
return u+v+'';
}
var f = generator()
f.next()
hello generator...
Object { value: "ni", done: false }
f.next()
Object { value: "undefinedtest", done: false }
f.next()
Object { value: "NaN", done: true }
//传值的情况
var z = generator();
z.next();
hello generator...
Object { value: "ni", done: false }
z.next('frist');
Object { value: "fristtest", done: false }
z.next('second');
Object { value: "secondfrist", done: true }
因此我们利用这一特性来向generator函数内部注入值来控制函数的执行,如下所示
unction* gene(){
console.log('start generating...');
let ret = yield 'hello';
if(ret == 'a'){
yield 'a';
}else{
yield 'b';
}
return 'ending';
}
var g = gene();
g.next()
start generating...
Object { value: "hello", done: false }
g.next('c');
Object { value: "b", done: false }
g.next();
Object { value: "ending", done: true }
3.Generator实例方法throw
throw方法可以在函数体外抛出错误,然后在generator函数内部捕获错误,但同时只能一条错误异常,如下所示
function* catchGene(){
try{
yield 'try';
}catch(e){
console.log('generator函数内部捕获:'+e);
}
}
var g = catchGene();
try{
console.log(g.next());
g.throw('a');
g.throw('b');
}catch(e){
console.log('全局捕获:'+e);
}
Object { value: "try", done: false }
generator函数内部捕获:a
全局捕获:b
如果在generator函数体内没有部署try...catch语句,则generator实例throw抛出的错误不能被捕获,可以被全局catch捕获,如下所示
function* gen(){
yield 'hello';
yield 'world';
}
var g = gen();
try{
g.throw('a');
}catch(e){
console.log('全局捕获:'+e);
}
全局捕获:a
不管是generator实例throw方法或者throw命令抛出的错误,只要被捕获了就不会影响generator函数的next方法的执行,否则遍历直接终止,如下所示
function* gen(){
yield 'hello';
yield 'world';
}
var g = gen();
console.log(g.next());
g.throw();
console.log(g.next());
VM226:7 Object {value: "hello", done: false}
VM226:8 Uncaught undefined
-----------------------使用try...catch捕获-----------------
function* gen(){
try{
yield 'hello';
}catch(e){
console.log(e);
}
yield 'world';
yield 'ending';
}
var g = gen();
console.log(g.next());
console.log(g.throw('a'));
console.log(g.next());
Object { value: "hello", done: false }
a
Object { value: "world", done: false }
Object { value: "ending", done: false }
特别注意的是catch捕获到错误后,继续执行到下一个yield语句,相当于再执行了一个next方法。
generator函数内部抛出的错误,可以被函数体外的catch捕获,这时由于报错,JS引擎认为generator函数遍历完毕,之后再调用next都是返回{value:undefined,done:true}对象,如下所示
function* gen(){
yield 'hello';
yield x+y;
yield 'world';
}
var g = gen();
console.log(g.next());
try{
console.log(g.next());
}catch(e){
console.log(e);
}
console.log(g.next());
Object { value: "hello", done: false }
ReferenceError: x is not defined
堆栈跟踪:
gen@debugger eval code:3:2
@debugger eval code:9:14
Object { value: undefined, done: true }
4.Generator实例方法return
该方法会返回给定的值,并终止generator函数的遍历,如下所示
function* gen(){
yield 'hello';
yield 'world';
}
var g = gen();
g.next()
Object { value: "hello", done: false }
g.return("return");
Object { value: "return", done: true }
g.next()
Object { value: undefined, done: true }
如果return方法没有给出任何值,则返回undefined,如果generator函数体内部部署了try...finally语句,return语句会被推迟到finally执行完后执行,如下所示
function* gen(){
try{
yield 'hello';
}finally{
yield 'world';
}
}
var g = gen();
g.next()
Object { value: "hello", done: false }
g.return("nihao")
Object { value: "world", done: false }
g.next()
Object { value: "nihao", done: true }
g.next()
Object { value: undefined, done: true }
5.yield*语句
yield*语句用在generator函数内部执行另一个遍历器对象,如下所示
function* letter(){
yield 'b';
yield 'c';
yield 'd';
}
function* gen(){
yield "a";
letter(); //直接调用没有效果
yield "e";
}
[...gen()]
Array [ "a", "e" ]
------------------------------------------
function* letter(){
yield 'b';
yield 'c';
yield 'd';
}
function* gen(){
yield "a";
yield* letter();//yield* 语句
yield "e";
}
[...gen()]
Array [ "a", "b", "c", "d", "e" ]
只要实现了Iterator接口的对象都可以使用yield*遍历,如下所示
function* gen(){
yield 1;
yield 2;
yield* [3,4,5,6,7];
yield 10;
}
console.log([...gen()]);
Array [ 1, 2, 3, 4, 5, 6, 7, 10 ]
----------------------------遍历嵌套函数-----------------------
function* walkArr(arr){
if(Array.isArray(arr)){
for(let v of arr){
yield* walkArr(v);
}
}else{
yield arr;
}
}
var w = walkArr([1,[2,[3,10,[9]]]]);
[...w];
Array [ 1, 2, 3, 10, 9 ]
Generator函数就介绍到此咯
ES6 - Note7:Generator函数的更多相关文章
- ES6的generator函数
generator是什么? generator是ES6提供的一种异步编程解决方案,在语法上,可以把它理解为一个状态机,内部封装了多种状态.执行generator,会生成返回一个遍历器对象.返回的遍历器 ...
- ES6 学习 -- Generator函数
(1)语法说明:Generator函数其实是一个普通函数,其有两个特点,一是,function关键字与函数名之间有一个星号(*):二是Generator函数内部使用yield表达式,定义不同的状态,然 ...
- 【es6】Generator 函数
1. 基本概念 状态机,封装了多个内部状态 2. 应用 返回一个遍历器对象. 3. 代码形式 function* helloWorldGenertor() { yield 'hello'; yield ...
- ES6入门之Generator函数
Generator Generator函数是ES6提供的一种异步编程解决方案,Generator函数是一个状态机,封装了多个内部状态. 执行Generator函数会返回一个遍历器对象,也就是说,Gen ...
- 转: ES6异步编程:Generator 函数的含义与用法
转: ES6异步编程:Generator 函数的含义与用法 异步编程对 JavaScript 语言太重要.JavaScript 只有一根线程,如果没有异步编程,根本没法用,非卡死不可. 以前,异步编程 ...
- es6 generator函数
es6 新增了Generator函数,一种异步编程的解决方案 回顾一下,es6 提供了新的遍历方法,for of ,适用于各种数据集合,统一了遍历操作,原生支持for of 集合的数据集合有.数组,字 ...
- ES6必知必会 (七)—— Generator 函数
Generator 函数 1.Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同,通常有两个特征: function关键字与函数名之间有一个星号: 函数体内部使 ...
- ES6的新特性(17)——Generator 函数的异步应用
Generator 函数的异步应用 异步编程对 JavaScript 语言太重要.Javascript 语言的执行环境是“单线程”的,如果没有异步编程,根本没法用,非卡死不可.本章主要介绍 Gener ...
- ES6的新特性(16)——Generator 函数的语法
Generator 函数的语法 简介 基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同.本章详细介绍 Generator 函数的语法和 API,它的 ...
随机推荐
- 使用VisualVM分析性能
性能分析神器VisualVM VisualVM 是一款免费的,集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优.这些功能包括生成和分析海量 ...
- 了解vmware tools
了解vmware tools vmware tools是虚拟机VMware Workstation自带的一款工具,它的作用就是使用户可以从物理主机直接往虚拟机里面拖文件.如果不安装它,我们是无法进行虚 ...
- Spotlight监控Oracle数据库的链接创建
最近在做性能测试时,由于要挂载空间数据,开发人员直接将所有业务表都挂到了Oracle数据库中.最近做了几次测试发现响应时间和吞吐量都不是很理想,进行一番分析后怀疑可能在Oracle中出现问题,因此再网 ...
- spark - tasks is bigger than spark.driver.maxResultSize
Error ERROR TaskSetManager: Total size of serialized results of 8113 tasks (1131.0 MB) is bigger tha ...
- Jtable 表格按多列排序(支持中文汉字排序)
这两天公司让做一个Jtable表格的排序,首先按A列排序,在A列相等时按B列排序,B列相等时按C列排序,ABC三列可以任意指定,最多分三列,这样的一个需求.由于我是大神,所以必须做了出来.ok,不自恋 ...
- Java面向对象课程小结
1.什么是对象?什么是类? 类和对象的关系 定义类的方法 方法五要素 new关键字 引用类型变量的赋值 null和nullException null大小写的区别 2.方法 重载和重写 构造 方法,带 ...
- HTML打折计算价格
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <met ...
- 你必须知道的指针基础-4.sizeof计算数组长度与strcpy的安全性问题
一.使用sizeof计算数组长度 1.1 sizeof的基本使用 如果在作用域内,变量以数组形式声明,则可以使用sizeof求数组大小,下面一段代码展示了如何使用sizeof: ,,,,,}; int ...
- 日志系统实战(一)—AOP静态注入
背景 近期在写日志系统,需要在运行时在函数内注入日志记录,并附带函数信息,这时就想到用Aop注入的方式. AOP分动态注入和静态注入两种注入的方式. 动态注入方式 利用Remoting的Context ...
- 浅谈SQL Server数据库分页
数据库分页是老生常谈的问题了.如果使用ORM框架,再使用LINQ的话,一个Skip和Take就可以搞定.但是有时由于限制,需要使用存储过程来实现.在SQLServer中使用存储过程实现分页的已经有很多 ...