浅析KOA(1)

原由

起步学node比较晚,因此没看express,直接看了koa,可是多数组件都是经过callback回调来执行,没法直接在koa中使用,因此了解了下koa的实现原理,以便包装现有组件或方法,这里记录下来细节,以做备份,并给你们作个参考吧。html

koa依赖co,配合thunkify来执行,虽然co如今已经改为基于promise实现了,但其实原理基本差很少,我这里先分析下co3.x版本。node

( 这里须要你对 generator 有必定了解 )es6

分析

核心是利用generator能够挂起或next(res)来继续执行方法的特性。express

这里不考虑异常处理流程,结合适当伪码,给出一个简易的demo便于理解co的控制流程:json

常规异步嵌套代码:api

fs.readFile('template.html', function (err, template) { // 读取模板
  asyncFunc('data.json', function (err, data) { // 获取数据
    rep.end(render(template, data)); // 装配并返回
  });
});

(注:上面的代码仅做示意,并不考虑效率因素。实际上若是模板和数据都是异步的,咱们应该同时发起请求,co推荐将异步操做组成数组或对象,它在内部会作平行处理以便优化速度)数组

咱们指望的同步写法:promise

var tempalte = readFile('template.html');
var data = asyncFunc('data.json');
rep.end(render(template, data));

为了实现这个功能,咱们利用generator的特性,先作一个工具方法迭代generator:app

function core(genfunc) {
  var g = genfunc();

  (function next(err, res) {
    res = g.next(res);
    if (!res.done) {
      res.value(next);
    }
  })();
}

上面这个方法接收一个generatorFunction做为参数,而后实例化它生成迭代器g,next方法不停的反复调用g,并将上一次的返回值做为参数不停的传递下去(有点像promise链式调用)。这里有个地方注意:生成器每次返回的值必须是一个方法,并接受fn回调参数,这样next方法才能做为参数不停的迭代下去(co支持多种数据类型,它自行包装了这些数据类型,最终仍是返回方法)koa

那么下面就差最后一步了,包装node标准api中的异步方法,以便支持yield:

function thunkify(fn) {
  return function () {
    var args = Array.prototype.slice.call(arguments, 0);
    return function (next) {
      fn.apply(this, args.concat(next));
    }
  }
}
// 包装原生API
var readFile = thunkify(fs.readFile);

如今当调用readFile('path')的时候返回的是一个方法了,也就是yield的返回值,经过在core方法中,咱们来调用这个方法,并将next做为参数传递给它,因而整个流程就能够以同步方式执行下去了,咱们将刚才的同步代码经过core进行包装,最终获得:

core(function*() {
  var tempalte = yield readFile('template.html');
  var data = yield asyncFunc('data.json');
  rep.end(render(template, data));
});

结论

这里asyncFunc我用来表示任何异步方法,它应该与readFile相似,返回另外一个方法并接受callback回调。标准API一般能够经过thunkify来直接wrap,但若是是本身实现的方法必需要遵照这个约定。

以上代码依赖generator特性,须要安装最新版的node,当前最新版本是0.11.15,经过--harmony参数启动,推荐安装iojs,支持es6多数语法,用来学习了解新语法不错。

相关文章
相关标签/搜索