Koa源码分析(一) -- generator

Abstract

本系列是关于Koa框架的文章,目前关注版本是Koa v1。主要分为如下几个方面:
1. Koa源码分析(一) -- generator
2. Koa源码分析(二) -- co的实现
3. Koa源码分析(三) -- middleware机制的实现html

Genetator函数

Generator函数是ES6提供的一种异步编程解决方案,其语法行为彻底不一样于传统函数。python

详细解析可见阮一峰老师的《ECMAScript 6 入门 --- Generator》es6

语法

两大特征编程

  1. function 关键字与函数名之间的 *
  2. 函数体内部使用 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 对象,这等同于断点就进入 subGeneratorsubGen里面,等待 subGen 所有执行完后再回来继续执行,相似于递归。
直白点说,就是将 subGen 内嵌到 mainGen中。

subGen函数中的return语句并不会起到断点的做用

结束语

Generator函数做为ES6的新特性,经过它能够很好的解决JavaScript中的恶魔回调问题。Koa框架使用这特性很好组织了异步代码的结构。

相关文章
相关标签/搜索