本系列是关于Koa框架的文章,目前关注版本是Koa v1。主要分为如下几个方面:
1. Koa源码分析(一) -- generator
2. Koa源码分析(二) -- co的实现
3. Koa源码分析(三) -- middleware机制的实现html
Generator函数是ES6提供的一种异步编程解决方案,其语法行为彻底不一样于传统函数。python
详细解析可见阮一峰老师的《ECMAScript 6 入门 --- Generator》es6
两大特征编程
function
关键字与函数名之间的 *
yield
语句咱们定义一个generatorFunction示例:框架
function* firstGenerator() { var one = yield 'one'; console.log(one); var two = yield 'two'; concole.log(two); var third = yield 'third'; console.log(third); return 'over' }
带有 *
的函数声明就表明 firstGenerator
函数是一个generator函数,函数里面的 yield 关键字能够理解为在当前位置设置断点,这一点若有疑问,能够看后续。异步
那generator函数的语法行为究竟与传统函数不一样在哪里呢?下边咱们来梳理下generator函数的运行步骤。异步编程
var it = firstGenerator(); console.log(it.next(1)); // {value: "one", done: false} console.log(it.next(2)); // {value: "two", done: false} console.log(it.next(3)); // {value: "third", done: false} console.log(it.next(4)); // {value: "over", done: true}
首先经过执行 firstGenerator
函数,咱们能够获得一个generator对象 it
,它是一个迭代器对象。此时, firstGenerator
函数并未执行,只是返回了迭代器对象 it
,咱们能够经过 it
对象中 next
方法触发 firstGenerator
函数开始执行,此时咱们调用 it.next(1)
,注意 next
注入的参数 1
并无任何效果。当 firstGenerator
函数执行到 yield
语句时,被打了断点,停留在此处,并将 yield
后的变量传递给 it.next(1)
结果对象中的 value
字段,另外其中的 done
字段表示 firstGenerator
函数还没有彻底执行完,还停留在断点。以此同时,将执行权交换给 it.next(1)
。函数
执行第二次 it.next(2)
,执行权再次交给 firstGenerator
函数,并将 next
带入的参数传递给函数中的变量 one
,此时输出 2
。当运行到 yield 'two'
语句时,再次将执行权返回给 it.next(2)
并传值。源码分析
第三次执行 it.next(3)
的过程与第二次彻底同样。code
最后一次执行 it.next(4)
时,在此以前, firstGenerator
函数断点在 var third = yield 'third'
,当 it.next(4)
将执行权交给 firstGenerator
函数时,将 4
传递给变量 third
,此刻输出 4
。当执行到 return
语句时,整个函数已经执行完了,并将 'over'
传递给 it.next(4)
返回结果中的 value
字段,且 done
字段为 true
。若没有 return
语句,则 value
字段返回 null
。
这样下来,整个 firstGenerator
整个函数执行完毕。咱们能够将Generator函数比喻成懒惰的癞蛤蟆,每次都须要使用it.next()方法戳一下,才会有对应的行动。若是你们了解python中协程的概念,应该很好理解Generator函数的语法行为。
在Generator函数中 yield
的语法,其后边的值也能够是函数、对象等等。 yield
后边能够是另外一个Generator对象。
function* subGen() { console.log('step in sub generator'); var b = yield 'sub 1'; console.log(b); console.log('step out sub generator'); } var subGenerator = new subGen(); function* mainGen() { var a = yield 'main 1'; console.log(a); var b = yield *subGenerator; console.log(b); var c = yield 'main 2'; console.log(c); return 'over'; } var it = mainGen(); console.log(it.next(1)); // {value: 'main 1', done: false} console.log(it.next(2)); // 2 // step in sub generator // {value: 'sub 1', done: false} console.log(it.next(3)); // 3 // step out sub generator // null // {value: 'main 2', done: false} console.log(it.next(4)); // 4 // {value: 'over', done: true}
yield
后面跟着 *subGenerator
对象,这等同于断点就进入 subGenerator
的 subGen
里面,等待 subGen
所有执行完后再回来继续执行,相似于递归。
直白点说,就是将 subGen
内嵌到 mainGen
中。
在subGen
函数中的return语句并不会起到断点的做用
Generator函数做为ES6的新特性,经过它能够很好的解决JavaScript中的恶魔回调问题。Koa框架使用这特性很好组织了异步代码的结构。