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函数的更多相关文章

  1. ES6的generator函数

    generator是什么? generator是ES6提供的一种异步编程解决方案,在语法上,可以把它理解为一个状态机,内部封装了多种状态.执行generator,会生成返回一个遍历器对象.返回的遍历器 ...

  2. ES6 学习 -- Generator函数

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

  3. 【es6】Generator 函数

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

  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. 使用VisualVM分析性能

    性能分析神器VisualVM VisualVM 是一款免费的,集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优.这些功能包括生成和分析海量 ...

  2. 了解vmware tools

    了解vmware tools vmware tools是虚拟机VMware Workstation自带的一款工具,它的作用就是使用户可以从物理主机直接往虚拟机里面拖文件.如果不安装它,我们是无法进行虚 ...

  3. Spotlight监控Oracle数据库的链接创建

    最近在做性能测试时,由于要挂载空间数据,开发人员直接将所有业务表都挂到了Oracle数据库中.最近做了几次测试发现响应时间和吞吐量都不是很理想,进行一番分析后怀疑可能在Oracle中出现问题,因此再网 ...

  4. 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 ...

  5. Jtable 表格按多列排序(支持中文汉字排序)

    这两天公司让做一个Jtable表格的排序,首先按A列排序,在A列相等时按B列排序,B列相等时按C列排序,ABC三列可以任意指定,最多分三列,这样的一个需求.由于我是大神,所以必须做了出来.ok,不自恋 ...

  6. Java面向对象课程小结

    1.什么是对象?什么是类? 类和对象的关系 定义类的方法 方法五要素 new关键字 引用类型变量的赋值 null和nullException null大小写的区别 2.方法 重载和重写 构造 方法,带 ...

  7. HTML打折计算价格

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <met ...

  8. 你必须知道的指针基础-4.sizeof计算数组长度与strcpy的安全性问题

    一.使用sizeof计算数组长度 1.1 sizeof的基本使用 如果在作用域内,变量以数组形式声明,则可以使用sizeof求数组大小,下面一段代码展示了如何使用sizeof: ,,,,,}; int ...

  9. 日志系统实战(一)—AOP静态注入

    背景 近期在写日志系统,需要在运行时在函数内注入日志记录,并附带函数信息,这时就想到用Aop注入的方式. AOP分动态注入和静态注入两种注入的方式. 动态注入方式 利用Remoting的Context ...

  10. 浅谈SQL Server数据库分页

    数据库分页是老生常谈的问题了.如果使用ORM框架,再使用LINQ的话,一个Skip和Take就可以搞定.但是有时由于限制,需要使用存储过程来实现.在SQLServer中使用存储过程实现分页的已经有很多 ...