深入探讨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)简介 什么是生成器? 我们从一个示例开始: ...
随机推荐
- ios5 中文键盘高度变高覆盖现有ui问题的解决方案(获取键盘高度的方法)(转载)
背景: ios5之前,iphone上的键盘的高度是固定为216.0px高的,中文汉字的选择框是悬浮的,所以不少应用都将此高度来标注键盘的高度(包括米聊也是这么做的). 可是在ios5中,键盘布局变了, ...
- $.getJSON 返回值、AJAX异步调用步骤
//首先要判断用户录入的手机号是不是中国移动的,不是直接给出提示,并终止登录 if($("#cmUsername1").val().isMobile())//手机号码 { jQue ...
- tomcat服务器配置多个项目
修改tomcat的server.xml文件中的Engine标签下的Host标签如下: <Host name="www.a.com" appBase="webapps ...
- MongoDB项目中常用方法
使用MongoDB连接池MongoOptions来进行连接 以及相关方法的调用 //获得驱动地址(这里的驱动 写入了配置文件中) String serverAddressStr = Configure ...
- json字符串相关转换方法
/** json转换为Map * @param jsonStr json * @return map集合 */ public static HashMap<String, String> ...
- do break的妙用
#include <stdio.h> #include <malloc.h> int func(int n) { //资源的统一申请 ; ; int* p = (int*)ma ...
- M2M协议
1, M2M协议 M2M是Machine-to-Machine/Man的简称,是一种以机器终端智能交互为核心的.网络化的应用与服务.M2M协议规定了人机和机器之间交互需要遵从的通信协议.随着科学技术的 ...
- redmine后台运行命令
nohup ruby script/rails server webrick -e production & redmine 3 后台运行命令: nohup bundle exec rail ...
- 30天轻松学习javaweb_http头信息实例
package com.wzh.test.http; import java.io.ByteArrayOutputStream;import java.io.IOException;import ja ...
- Tesseract-OCR 字符识别---样本训练
Tesseract是一个开源的OCR(Optical Character Recognition,光学字符识别)引擎,可以识别多种格式的图像文件并将其转换成文本,目前已支持60多种语言(包括中文). ...