此博客不再维护,博客已迁移至 https://github.com/purplebamboo/blog/issues
文章目录
  1. 1. generator的es6规范。
  2. 2. 进阶知识:generator的Delegating yield
  3. 3. 结语

koa是TJ大神新一代的中间件框架,本系列旨在一步一步实现koa的功能,包括下面这些。

  1. koa源码分析系列(一)generator
  2. koa源码分析系列(二)co的实现
  3. koa源码分析系列(三)koa的中间件机制实现
  4. koa源码分析系列(四)co-4.0新变化

koa基于co实现,co又是使用了es6的generator特性,所以,没错这个特性支持很一般。
有下面几种办法体验generator:

  • node v0.11 可以使用 (node —harmony)
  • 使用gnode来使用,不过据说性能一般
  • 使用chrome体验,打开chrome://flags/, 搜索harmony, 启用,重启chrome即可。

generator的es6规范。

什么是generator?generator是javascript1.7的内容,是 ECMA-262 在第六个版本,即我们说的 Harmony 中所提出的新特性。

首先我们先定义一个generatorFunction:

1
2
3
4
5
6
7
8
9
10
function* start() {
  var a = yield 'start';
  console.log(a);
  var b = yield 'running';
  console.log(b);
  var c = yield 'end';
  console.log(c);
  return 'over';
}
console.log(start.constructor.name) //"GeneratorFunction"

带有 *的函数声明即代表是一个GeneratorFunction,GeneratorFunction里面可以使用yield关键字,可以理解为在当前位置设置断点。

下面我们获得一个generator并且使用它

1
2
3
4
5
var it = start();
console.log(it.next());//Object {value: "start", done: false}
console.log(it.next(22));//22  object {value: 'running', done: false}
console.log(it.next(333));//333 Object {value: 'end', done: false}
console.log(it.next(444));//444 Object {value: "over", done: true}

通过执行GeneratorFunction我们可以得到一个generator对象也就是it。it对象有一个next方法。
当我们执行start()的时候,start里面的代码并没有执行。当我们第一次调用it.next()的时候代码会执行到第一个yield声明的地方。也就是var a = yield 'start';,注意这边只是执行到了赋值语句的右边yield部分,换句话说var a =这个赋值语句还没有执行。
此时it.next()返回的是一个对象,value是yield语句的值,这个值可以是字符串,函数,对象等等等,done代表当前的GeneratorFunction是否执行完毕。

也许你注意到了我们后来调用了it.next(22)给next传了一个参数。这个时候var a =赋值语句开始执行,实际上此时yield 'start'返回的就是22,也就是我们传的参数。一直执行到yield 'running';代码再次断点停住了。next方法的参数会成为上一个yield的返回值。

最后当执行到return 'over';的时候,next(444)返回的对象的done为true,代表整个代码执行完毕。

es6的generator的规范可以点这里,可惜由于本身generator还没有正式定稿,所以一直在修改中.前面的wiki也没有更新。目前来说wiki里面提到的send和close方法都已经移除了。变更记录可以在v8的issue里找到。https://code.google.com/p/v8/issues/detail?id=2715

进阶知识:generator的Delegating yield

Delegating yield是generator的进阶内容。代表一个代理的yield。
前面提到yield后面的值可以是函数,对象,等等。其实yield后面还可以这么用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function* run() {
console.log("step in child generator")
var b = yield 'running';
console.log(b);
console.log("step out child generator")

}
var runGenerator = run();
function* start() {
  var a = yield 'start';
  console.log(a);
  yield *runGenerator;
  var c = yield 'end';
  console.log(c);
  return 'over';
}
var it = start();
console.log(it.next());//Object {value: "start", done: false}
console.log(it.next(22));//22 step in child generator  object {value: 'running', done: false}
console.log(it.next(333));//333 step out child generator  Object {value: 'end', done: false}
console.log(it.next(444));//444 Object {value: "over", done: true}

yield后面可以跟 *anothergenerator,这样当前的断点就会进入到anothergenerator的generatorfunction里面,等子generator全部执行完后再回来继续执行。这个其实有点类似递归的意思。

其实说白了上面的代码跟之前的是等价的。yield*generator其实相当于把子generator的generatorfunction的代码混入了进来。

另外子generatorfunction的return值会做为yield*generator的返回值。
实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function* run() {
console.log("step in child generator");
return "child over";
var b = yield 'running';
console.log(b);
console.log("step out child generator")

}
var runGenerator = run();
function* start() {
  var a = yield 'start';
  console.log(a);
  var childValue = yield *runGenerator;
  console.log("childValue=="+childValue);
  var c = yield 'end';
  console.log(c);
  return 'over';
}
var it = start();
console.log(it.next());
//Object {value: "start", done: false}
console.log(it.next(22));
//22
//step in child generator
//childValue==child over
//Object {value: "end", done: false}
console.log(it.next(333));
//333 Object {value: "over", done: true}

简单的测试,子generator的generatorFunction里面如果有return的话,下面的断点就不再起作用,而是提前返回,并且return的值 作为代理调用的返回值。

结语

generator是es6的一个新特性,支持还不是很好,但是这并不影响它的成名,因为通过它可以很好的解决javascript的“恶魔”回调问题。基本generator的功能都介绍了。通过设置断点,我们将可以很好的将回调解放出来,目前比较知名的就是TJ的co库了,下篇,我将按照co的原理实现异步编程的一个简陋库。

文章目录
  1. 1. generator的es6规范。
  2. 2. 进阶知识:generator的Delegating yield
  3. 3. 结语