深入探讨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)简介 什么是生成器? 我们从一个示例开始: ...
随机推荐
- Android开发问题笔记
1.Toolbar问题:最低版本15,必须使用support,才能使用Toolbar,Toobar是5.0引入的 2.BottomTab:这个用TabLayout解决了 3.后端API最好采用一个成熟 ...
- AP_AP系列 - 供应商管理(案例)
2014-07-03 Created By BaoXinjian
- 帝国cms常用变量总结
一.常用变量 当前栏目ID $GLOBALS['navclassid'] 当前父栏目ID $class_r[$cid]['bclassid'] 栏目路径 $class_r[栏目ID]['classpa ...
- git相关网页
git.apache2.gerrit安装 1.http://blog.csdn.net/benkaoya/article/details/8680886 2.http://fatalove.iteye ...
- (转)关于rdlc报表的数据源
rdlc 报表字符类数据分为文本数据和表数据,区别就在于文本数据只有一个,表数据可以有多行,然而有很多数据只需要一个传入就可以比如打印某个用户的基本信息,很多信息都是唯一的,如果此时报表传入的数据 ...
- js中如何操作json数据
一.要想熟练的操作json数据,就先要了解json数据的结构,json有两种结构:对象和数组. 1.对象 一个对象以“{”开始,“}”结束.每个“名称”后跟一个“:”:“‘名称/值’ 对”之间使用“, ...
- spring整合struts
整合目标:使用spring的bean管理struts action service. 整合步骤: 一.加入spring 1.加入spring jar包 2.配置web.xml文件 <contex ...
- 理论沉淀:隐马尔可夫模型(Hidden Markov Model, HMM)
理论沉淀:隐马尔可夫模型(Hidden Markov Model, HMM) 参考链接:http://www.zhihu.com/question/20962240 参考链接:http://blog. ...
- 在Yarn上运行spark-shell和spark-sql命令行
转载自:http://lxw1234.com/archives/2015/08/448.htm 如果你已经有一个正常运行的Hadoop Yarn环境,那么只需要下载相应版本的Spark,解压之后做为S ...
- LPC1788 SDRAM运行程序
折腾了很久 终于解决了 从SDRAM中运行APP程序. 说明:LPC1788 本身有512K的flash和96K的RAM.支持TFT和SDRAM 这算是跟别家cortex-M3架构MCU相比较的一个亮 ...