@cherishpeace
2014-05-17T18:17:00.000000Z
字数 3357
阅读 1585
koa是TJ大神新一代的中间件框架,本系列旨在一步一步实现koa的功能,包括下面这些。
1. koa源码分析系列(一)generator
2. koa源码分析系列(二)co的实现
3. koa源码分析系列(三)koa的中间件机制实现
koa基于co实现,co又是使用了es6的generator特性,所以,没错这个特性支持很一般。
有下面几种办法体验generator:
什么是generator?generator是javascript1.7的内容,是 ECMA-262 在第六个版本,即我们说的 Harmony 中所提出的新特性。
首先我们先定义一个generatorFunction:
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并且使用它
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
Delegating yield是generator的进阶内容。代表一个代理的yield。
前面提到yield后面的值可以是函数,对象,等等。其实yield后面还可以这么用。
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的返回值。
实例如下:
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的原理实现异步编程的一个简陋库。