[关闭]
@cherishpeace 2014-05-17T10:17:00.000000Z 字数 3357 阅读 1467

koa源码分析系列(一)generator

koa是TJ大神新一代的中间件框架,本系列旨在一步一步实现koa的功能,包括下面这些。
1. koa源码分析系列(一)generator
2. koa源码分析系列(二)co的实现
3. koa源码分析系列(三)koa的中间件机制实现

环境准备

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

generator的es6规范。

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

首先我们先定义一个generatorFunction:

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

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

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

  1. var it = start();
  2. console.log(it.next());//Object {value: "start", done: false}
  3. console.log(it.next(22));//22 object {value: 'running', done: false}
  4. console.log(it.next(333));//333 Object {value: 'end', done: false}
  5. 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. function* run() {
  2. console.log("step in child generator")
  3. var b = yield 'running';
  4. console.log(b);
  5. console.log("step out child generator")
  6. }
  7. var runGenerator = run();
  8. function* start() {
  9. var a = yield 'start';
  10. console.log(a);
  11. yield *runGenerator;
  12. var c = yield 'end';
  13. console.log(c);
  14. return 'over';
  15. }
  16. var it = start();
  17. console.log(it.next());//Object {value: "start", done: false}
  18. console.log(it.next(22));//22 step in child generator object {value: 'running', done: false}
  19. console.log(it.next(333));//333 step out child generator Object {value: 'end', done: false}
  20. 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. function* run() {
  2. console.log("step in child generator");
  3. return "child over";
  4. var b = yield 'running';
  5. console.log(b);
  6. console.log("step out child generator")
  7. }
  8. var runGenerator = run();
  9. function* start() {
  10. var a = yield 'start';
  11. console.log(a);
  12. var childValue = yield *runGenerator;
  13. console.log("childValue=="+childValue);
  14. var c = yield 'end';
  15. console.log(c);
  16. return 'over';
  17. }
  18. var it = start();
  19. console.log(it.next());
  20. //Object {value: "start", done: false}
  21. console.log(it.next(22));
  22. //22
  23. //step in child generator
  24. //childValue==child over
  25. //Object {value: "end", done: false}
  26. console.log(it.next(333));
  27. //333 Object {value: "over", done: true}

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

结语

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

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注