深入探讨ES6生成器
如果对于ES6生成器不熟悉,请先阅读并运行下http://www.cnblogs.com/linda586586/p/4282359.html里面的代码。当你感觉掌握了基础之后,我们可以深入探讨一些细节。
错误处理
在ES6生成器设计中最强大的是一个生成器内部代码的语义是同步的,即使外部迭代控制是异步的。
可以使用简单的也许你很熟悉的错误处理技术--就是try-catch机制。
例如:
function *foo() {
try {
var x = yield 3;
console.log( "x: " + x ); // may never get here!
}
catch (err) {
console.log( "Error: " + err );
}
}
即使函数会在yield3处暂停,也许会保持暂停状态任意一段时间,如果回传给生成器错误,try-catch会捕获它!试着使用普通异步方法处理,像回调函数。
但是,错误回传给这个生成器有多精确?
var it = foo(); var res = it.next(); // { value:3, done:false } // instead of resuming normally with another `next(..)` call,
// let's throw a wrench (an error) into the gears:
it.throw( "Oops!" ); // Error: Oops!
可以看到,在迭代器中用到的另外一个方法--throw(),会向生成器抛错,就好像正好发生在生成器yield暂停位置的点上。try-catch语句就像所期望的那样捕获了错误!
注:如果向生成器内抛错,但是没有try-catch捕获它,错误会传播回去。所以:
function *foo() { } var it = foo();
try {
it.throw( "Oops!" );
}
catch (err) {
console.log( "Error: " + err ); // Error: Oops!
}
显然,反方向的错误处理也起作用了:
function *foo() {
var x = yield 3;
var y = x.toUpperCase(); // could be a TypeError error!
yield y;
} var it = foo(); it.next(); // { value:3, done:false } try {
it.next( 42 ); // `42` won't have `toUpperCase()`
}
catch (err) {
console.log( err ); // TypeError (from `toUpperCase()` call)
}
代理生成器
另外想要做的是从生成器函数内部调用另外一个生成器。这意思不仅仅是用普通的方法实例化生成器,实际上是对于其他生成器代理迭代控制。为了这样做,可以使用yield关键字的一个变形:yield *("yield star").例子:
function *foo() {
yield 3;
yield 4;
} function *bar() {
yield 1;
yield 2;
yield *foo(); // `yield *` delegates iteration control to `foo()`
yield 5;
} for (var v of bar()) {
console.log( v );
}
// 1 2 3 4 5
就像前面一篇介绍的,这里也使用yield *foo()代替了其他文章里面的yield* foo()。我认为它可以更确切的说明在发生的事情。
让我们看看这个是怎么工作的。yield 1和yield 2直接把他们的值送出到了for of循环的next()调用,像我们所了解和期望的那样。
但是yield*被碰到了,你会注意到我们正通过实例化foo()进入另外一个生成器.所以我们在为另外一个生成器迭代进行代理。
当yield*从*bar()到*foo()代理时,for-of循环的next()调用是控制foo(),然而yield 3和yield4将他们的值送出去到for-of循环。
当*foo()结束的时候,控制返回到原始的生成器中,最后调用yield 5。
简化下,这个例子只向外yields值。但是当然,如果不用for-of循环,只是手动调用迭代器的next(),并且传递信息到里面,那些信息会以同样期望的方式通过yield*代理传递。
function *foo() {
var z = yield 3;
var w = yield 4;
console.log( "z: " + z + ", w: " + w );
} function *bar() {
var x = yield 1;
var y = yield 2;
yield *foo(); // `yield*` delegates iteration control to `foo()`
var v = yield 5;
console.log( "x: " + x + ", y: " + y + ", v: " + v );
} var it = bar(); it.next(); // { value:1, done:false }
it.next( "X" ); // { value:2, done:false }
it.next( "Y" ); // { value:3, done:false }
it.next( "Z" ); // { value:4, done:false }
it.next( "W" ); // { value:5, done:false }
// z: Z, w: W it.next( "V" ); // { value:undefined, done:true }
// x: X, y: Y, v: V
即使我们只在这里演示了一层代理,*foo()没有理由不能为另外的生成器迭代器yield代理,然后再一个,以此类推。
另外一个yield可以玩的把戏是从代理生成器接收return值。
function *foo() {
yield 2;
yield 3;
return "foo"; // return value back to `yield*` expression
} function *bar() {
yield 1;
var v = yield *foo();
console.log( "v: " + v );
yield 4;
} var it = bar(); it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // "v: foo" { value:4, done:false }
it.next(); // { value:undefined, done:true }
可以看到,yield *foo()代理了迭代控制(next()调用)直到他结束,然后来自foo()的任何返回值被置为yield*表达式的结果,然后赋值给了本地变量v.
yield和yield *有一个有意思的区别:用yield表达式,结果是被随后的next()送进来的,但是用yield*,它只从它的代理的return值接受结果。
也可以从两个方向上进行错误处理,通过yield*代理:
function *foo() {
try {
yield 2;
}
catch (err) {
console.log( "foo caught: " + err );
} yield; // pause // now, throw another error
throw "Oops!";
} function *bar() {
yield 1;
try {
yield *foo();
}
catch (err) {
console.log( "bar caught: " + err );
}
} var it = bar(); it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false } it.throw( "Uh oh!" ); // will be caught inside `foo()`
// foo caught: Uh oh! it.next(); // { value:undefined, done:true } --> No error here!
// bar caught: Oops!
可以看到,throw("Uh oh!")在*foo()内通过yield*代理抛错到了try-catch。在*foo()内的throw "Oops!"抛出来回到了*bar(),然后通过另外一个try-catch捕获了。要是没有捕获到他们其中的一个,错误会像期望的那样继续向外传播。
总结
生成器有同步处理机制,即可以通过yield声明使用try-catch错误处理。生成器迭代器也有一个throw()方法在生成器暂停的位置抛错,当然也可以被生成器内部的try-catch捕获。
yield允许从当前的生成器为另外一个代理迭代控制。结果是yield*在两个方向上传递,可以是信息也可以是错误。
但是,还有一个基本问题遗留了,没有回答:生成器怎么通过同步代码模式帮助我们?所有我们现在看到的这两篇文章是生成器函数的同步迭代器。
关键是构建一个生成器暂停来启动异步任务的机制,然后在异步任务最后恢复。我们将探讨很多通过生成器生成这样异步控制的方法。
英文原文:http://davidwalsh.name/es6-generators-dive
深入探讨ES6生成器的更多相关文章
- 学习ES6生成器(Generator)
背景 在JS的使用场景中,异步操作的处理是一个不可回避的问题,如果不做任何抽象.组织,只是“跟着感觉走”,那么面对“按顺序发起3个ajax请求”的需求,很容易就能写出如下代码(假设已引入jQuery) ...
- ES6生成器基础
ES6引进的最令人兴奋的特性就是一种新的函数生成方式,称为生成器(generator).名称有点奇怪,但是第一眼看上去行为更加奇怪.文章主要介绍生成器如何工作,然后让你明白为什么他们对于未来的JS会有 ...
- 【翻译】ES6生成器简介
原文地址:http://davidwalsh.name/es6-generators ES6生成器全部文章: The Basics Of ES6 Generators Diving Deeper Wi ...
- ES6生成器函数generator
ES6生成器函数generator generator是ES6新增的一个特殊函数,通过 function* 声明,函数体内通过 yield 来指明函数的暂停点,该函数返回一个迭代器,并且函数执行到 y ...
- ES6生成器与迭代器
ES6迭代器的一个例子 function run(taskDef) { var task = taskDef(); var result = task.next(); // 递归执行迭代 functi ...
- 探讨ES6的import export default 和CommonJS的require module.exports
今天来扒一扒在node和ES6中的module,主要是为了区分node和ES6中的不同意义,避免概念上的混淆,同时也分享一下,自己在这个坑里获得的心得. 在ES6之前 模块的概念是在ES6发布之前就出 ...
- 轻松学会ES6新特性之生成器
生成器虽然是ES6最具魔性的新特性,但也是最难懂得的一节,笔者写了大量的实例来具体化这种抽象的概念,能够让人一看就懂,目的是希望别人不要重复或者减少笔者学习生成器的痛苦经历. 在说具体的ES6生成器之 ...
- ES6最具魔力的特性——生成器
ES6生成器(Generators)简介 我们从一个示例开始: function* quips(name) { yield "你好 " + name + "!" ...
- 深入浅出ES6(三):生成器 Generators
作者 Jason Orendorff github主页 https://github.com/jorendorff ES6生成器(Generators)简介 什么是生成器? 我们从一个示例开始: ...
随机推荐
- Keepalived高可用软件的安装与配置
监听和替换多台服务器之间的来回切换 一.安装tar zxvf keepalived-1.1.15.tar.gzcd keepalived-1.1.15./configure --prefix=/usr ...
- java: org.luaj.vm2.LuaError:XXX module not found lua脚本初始化出错
我遇到这个错误是因为在引用脚本目录时,设置错了位置.设置成脚本所在目录的上级目录. lua使用和加载初始化方法 在java中使用lua,使用需要引用 luaj-jse-2.0.2.jar 同时需要使用 ...
- PO_PO系列 - 采购单管理分析(案例)
2014-07-01 Created By BaoXinjian
- linux 命令(1)screen
一.screen的安装和用法 Screen是一个可以在多个进程之间多路复用一个物理终端的窗口管理器,这意味着你能够使用一个单一的终端窗口运行多终端的应用. Screen中有会话的概念,用户可以在一个s ...
- 查询oracle中所有用户信息
1.查看所有用户:select * from dba_users; select * from all_users; select * from user_users; 2.查看用户或角色系统 ...
- 帝国CMS 6.0功能解密之新版结合项功能,帝国结合项使用
可以用来做A-Z信息检索 某字段等于多少,输出 等等 帝国CMS6.0在继承以往版本结合项功能的基础上又新增很多特性,更强大.今天我们就专门来讲解6.0的结合项改进. 回顾下以往版本的结合项语 ...
- Eclipse控制台输出信息的控制
当你在Eclipse中 running/debugging一个应用程序的时候,有关该应用程序的运行调试信息及日志信息都会输出到控制台(console )显示,但是Eclipse只会显示最后一部分的日志 ...
- RightBarButon
//rightBar button UIButton *rightButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 34, 34)]; ...
- python 抓取javascript 动态数据
1. 新安装一个python库 :~$ sudo pip install seleniumhq 2. 编写代码: 以获取百度百科点赞数为例 import selenium from selenium ...
- 性能测试工具之Gatling
转载:http://ningandjiao.iteye.com/blog/2004579 Gatling一直是久闻其名但是未得机会运用,正好最近有需求做性能测试,于是趁此机会熟悉了一下,可以说,这是目 ...