// 使用 function* 定义一个 generator 函数
function* helloWorldGenerator() {
yield 'hello'; // yield 关键字作为暂停的点
yield 'world';
return 'ending';
} var hw = helloWorldGenerator(); // 执行 generator 函数,返回一个遍历器对象。而这时,这个函数内的代码并不会执行。
// 调用遍历器对象的 next 方法,执行函数内的代码,执行到下一个 yield 的位置,并暂停执行
hw.next()
// { value: 'hello', done: false } value 是 yield 后面跟的表达式的值,done 是 genertator 函数结束状态 // 再次调用 next,执行到下一个 yield 位置
hw.next()
// { value: 'world', done: false } // 执行结束,value 值为 return 的值,没有 return 则为 undefined(函数没 return 返回 undefined),done 变为 true
hw.next()
// { value: 'ending', done: true } // 还可以无限次调用 next,但是都返回相同的对象
hw.next()
// { value: undefined, done: true }

yield 不能用在普通函数中:

var flat = function* (a) {
// forEach 方法是个普通函数,在里面使用了 yield 会报错。解决方法是改为 for 循环
a.forEach(function (item) {
if (typeof item !== 'number') {
yield* flat(item);
} else {
yield item;
}
}
};

yield语句如果用在一个表达式之中,必须放在圆括号里面。

console.log('Hello' + yield); // SyntaxError
console.log('Hello' + yield 123); // SyntaxError console.log('Hello' + (yield)); // OK
console.log('Hello' + (yield 123)); // OK

next方法的参数

function* foo(x) {
var y = 2 * (yield (x + 1)); // yield 语句在表达式中,需要将 yield 语句括起来,否则报错
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:true} var b = foo(5);
b.next() // { value:6, done:false } 调用第一次 next 开始执行,得到第一个 yield 的返回值 6。由于 next 参数为上一个 yield 语句的值,所以第一个 next 传入参数没有意义
b.next(12) // { value:8, done:false } 调用 next 方法时注入了数据,作为上一个 yield 语句的值,得到 var y = 2 * 12
b.next(13) // { value:42, done:true } 得到 var z = 13

for...of循环

for...of循环可以自动遍历Generator函数时生成的Iterator对象,且此时不再需要调用next方法。

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 这里需要注意,一旦next方法的返回对象的done属性为truefor...of循环就会中止,且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for...of循环之中。

原生的JavaScript对象没有遍历接口,无法使用for...of循环,通过Generator函数为它加上这个接口,就可以用了。

// 第一种方法
function* objectEntries(obj) {
let propKeys = Reflect.ownKeys(obj); for (let propKey of propKeys) {
yield [propKey, obj[propKey]];
}
} let jane = { first: 'Jane', last: 'Doe' }; for (let [key, value] of objectEntries(jane)) {
console.log(`${key}: ${value}`);
} // 第二种方法
function* objectEntries() {
let propKeys = Object.keys(this); for (let propKey of propKeys) {
yield [propKey, this[propKey]];
}
} let jane = { first: 'Jane', last: 'Doe' }; jane[Symbol.iterator] = objectEntries; for (let [key, value] of jane) {
console.log(`${key}: ${value}`);
}

Generator.prototype.throw()

Generator函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在Generator函数体内捕获。

var g = function* () {
// 使用 try...catch... 进行异常捕获
try {
yield;
} catch (e) {
console.log('内部捕获', e);
}
}; var i = g();
i.next(); try {
i.throw('a'); // 这里使用 throw 方法抛出的错误,会由 generator 函数内的 catch 处理
i.throw('b'); // generator 内的 catch 已经执行过了,就不会再被 generator 的 catch 捕获了,由外部的 catch 捕获
} catch (e) {
console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b

如果Generator函数内部没有部署try...catch代码块,那么throw方法抛出的错误,将被外部try...catch代码块捕获。

如果Generator函数内部和外部,都没有部署try...catch代码块,那么程序将报错,直接中断执行。

throw方法被捕获以后,会附带执行下一条yield语句。也就是说,会附带执行一次next方法。

var gen = function* gen(){
try {
yield console.log('a');
} catch (e) {
// ...
}
yield console.log('b'); // throw 方法会附带执行 next,从而执行到这个 yield 位置
yield console.log('c');
} var g = gen();
g.next() // a
g.throw() // b
g.next() // c

Generator.prototype.return()

Generator函数返回的遍历器对象,还有一个return方法,可以返回给定的值,并且终结遍历Generator函数。

function* gen() {
yield 1;
yield 2;
yield 3;
} var g = gen(); g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true } value 值变成了 return 的参数
g.next() // { value: undefined, done: true } return 方法 导致 generator 函数结束,所以 value 为 undefined

yield*语句

function* foo() {
yield 'a';
yield 'b';
} function* bar() {
yield 'x';
// foo(); 如果只是单纯的执行 foo() 函数,只是得到一个遍历器对象,并不会产生什么效果。
yield* foo(); // 使用了 yield* 语句,在遍历的时候才会遍历这个 generator 函数内部的 generator 函数。
yield 'y';
} for (let v of bar()){
console.log(v);
}
// "x"
// "a"
// "b"
// "y"
function* gen(){
yield* ["a", "b", "c"]; // 数组、字符串等,带有 iterator 接口的,都可以被 yield* 遍历
} gen().next() // { value:"a", done:false }

Generator与状态机

var clock = function*() {
while (true) {
console.log('Tick!'); // 执行状态1代码
yield;
console.log('Tock!'); // 执行状态2代码
yield;
}
};

每次调用 next() 就可以在两种状态间切换执行,而不需要使用一个布尔变量来做判断

Generator 函数学习笔记的更多相关文章

  1. C++学习基础十六-- 函数学习笔记

    C++ Primer 第七章-函数学习笔记 一步一个脚印.循序渐进的学习. 一.参数传递 每次调用函数时,都会重新创建函数所有的形参,此时所传递的实参将会初始化对应的形参. 「如果形参是非引用类型,则 ...

  2. async 函数学习笔记

    async函数就是Generator函数的语法糖. var fs = require('fs'); var readFile = function (fileName) { return new Pr ...

  3. async 函数--学习笔记一

    含义: ES2017 标准引入了 async 函数,使得异步操作变得更加方便.async 函数是什么?一句话,它就是 Generator 函数的语法糖. 前文有一个 Generator 函数,依次读取 ...

  4. async函数学习笔记

    含义 async函数是什么?一句话,它就是Generator函数的语法糖. const fs = require('fs') const readFile = function(fileName){ ...

  5. contiki-main.c 中的process系列函数学习笔记 <contiki学习笔记之六>

    说明:本文依然依赖于 contiki/platform/native/contiki-main.c 文件. ---------------------------------------------- ...

  6. Swift2.0 函数学习笔记

    最近又有点忙,忙着找工作,忙着适应这个新环境.现在好了,上班两周周了,也适应过来了,又有时间安安静静的就行我们前面的学习了.今天这篇笔记,记录的就是函数的使用.下面这些代码基本上是理清楚了函数的额使用 ...

  7. MYSQL存储过程和函数学习笔记

    学至Tarena金牌讲师,金色晨曦科技公司技术总监沙利穆课程笔记的综合. 1. 什么是存储过程和函数 将SQL语句放入一个集合里,然后直接调用存储过程和函数来执行已经定义好的SQL语句,通过存储过程和 ...

  8. loss函数学习笔记

    一直对机器学习里的loss函数不太懂,这里做点笔记. 符号表示的含义,主要根据Andrew Ng的课程来的,\(m\)个样本,第\(i\)个样本为\(\vec x^{(i)}\),对应ground t ...

  9. jQuery 取消事件冒泡 阻止后续内容执行 闭包函数 (学习笔记)

    1.取消事件冒泡 <title>取消事件冒泡</title> <style> div { border:solid 1px black; } </style& ...

随机推荐

  1. [bzoj3555]企鹅QQ(hash)

    3555: [Ctsc2014]企鹅QQ Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1645  Solved: 616[Submit][Statu ...

  2. phonegap开发经验谈之一命令行建立项目和准备工作

    一安装与配置 安装命令行配置,这个可以参见网上的.3.0的最好用命令行配置. 大家在安装android sdk的时候,会发现里面自带了一个eclipse,并且继承了adt,直接用这个就好了.当然你已经 ...

  3. HDU-4528 小明系列故事——捉迷藏 BFS模拟

    题意:链接 分析:每一个D或者是E点往四面延伸,并且赋一个特殊的值,能看到D点的点赋值为1,能看到E点的点赋值为1000,这是因为最多100步,因此最后可以根据除以1000和对1000取模来得出某个状 ...

  4. [css] CSS相对定位|绝对定位

    第一篇链接:http://www.zhangxinxu.com/wordpress/2010/12/css-%E7%9B%B8%E5%AF%B9%E7%BB%9D%E5%AF%B9%E5%AE%9A% ...

  5. Hbase WAL线程模型源码分析

    版权声明:本文由熊训德原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/257 来源:腾云阁 https://www.qclo ...

  6. 关于JAVA EE项目在WEB-INF目录下的jsp页面如何访问WebRoot中的CSS和JS文件

    找了这么久资料,总算解决了 感谢博客园:http://www.cnblogs.com/xsht/p/5275081.html 感谢百度:http://zhidao.baidu.com/link?url ...

  7. GUI 测试

    图形用户界面( GUI )对软件测试提出了有趣的挑战,因为 GUI 开发环境有可复用的构件,开发用户界面更加省时而且更加精确.同时, GUI 的复杂性也增加了,从而加大了设计和执行测试用例的难度.因为 ...

  8. WCF双向通信,心跳

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入)Q  Q:408365330     E-Mail:egojit@qq.com WCF中双程 ...

  9. sqlite数据类型

    sqlite数据类型(时间 日期 double等)     sqlite3支持的数据类型: NULL.INTEGER.REAL.TEXT.BLOB但是,sqlite3也支持如下的数据类型smallin ...

  10. 【必备】史上最全的浏览器 CSS & JS Hack 手册(转)

    浏览器渲染页面的方式各不相同,甚至同一浏览器的不同版本(“杰出代表”是 IE)也有差异.因此,浏览器兼容成为前端开发人员的必备技能.如果有一份浏览器 Hack 手册,那查询起来就方便多了.这篇文章就向 ...