Koajs原理

本文地址 http://www.cnblogs.com/jasonxuli/p/5382090.html

Koajs让习惯阻塞式代码写法的同学感到很舒服,再也不用盖楼式的callback了,而且也不需要学习Promise的then,catch这些新东西。

但实际上,Koajs这样的写法有点像是语言的语法糖,它只不过把yield又包装成了Promise的链式调用。做这件事儿的库就是co库和compose库,compose库把koajs中的一堆中间件改装成了函数套娃,而co则把这些套娃改装成了Promise链。想要了解Koajs的原理,还真需要先了解一下Promise的基本概念。本文假设你已经了解了Promise的基本知识。

koajs的用法大概是这样的:

var koa = require('koa')();

koa.use(function* m1(next){
     ...
     yield next;
     ...
}
 
koa.use(function* m2(next){
     ...
     yield next;
     ...
}
 
koa .listen(3000);
 
这个koa.listen默认的callback是koa库的application.js中的app.callback,也就是在这个app.callback中,调用了关键的这一句:
var fn = this.experimental
? compose_es7(this.middleware)
: co.wrap(compose(this.middleware));
这也引出了co库和compose库这两个koajs的核心库。
 
先说compose库:
koa.use函数关键的代码就是这一句 
this.middleware.push(fn);
也就是说这个this.middleware是个中间件的数组。
实际上,compose是把[m1, m2, ...] 改装成了 m1(m2(m3(...))),以方便co库的下一步改装,如下:
 
function compose(middleware){
  return function *(next){
    var i = middleware.length;
    var prev = next || noop();
    var curr;

    while (i--) {
      curr = middleware[i];            // curr = m3
      prev = curr.call(this, prev);   // m3.call(this, null)  >>> m3() 即 prev=m3()
    }
 
    yield *prev;                             // m3()
  }
}
 
最后的结果相当于返回了一个函数套娃:m1(m2(m3))
 
这个结果作为co的参数,又发生了什么呢?
 
co lib:
Actually, co convert yields to promise thens.
 
// fake codes
function co(gen){
     if gen is a function
          gen = gen();
    
     if gen is null || gen is not a function
          return resolve(gen);
 
  onFulfilled();
 
     // onFulfilled() {
          try{
               var ret = gen.next();
          }catch(){
               reject(ret);
          }
    next();
     }
    
     // next() {
          if ret.done == true
               return resolve(ret);
 
               // toPromise(){
               if ret is a general value
                    return resove(ret)
               if ret is a generator   // 按深度延展: 处理 yield* 
                    return co(ret)
               }
 
          if (isPromise(value))
               return value.then(onFulfilled, onRejected);    // 按广度延展:处理下一个yield
     }
}
 
这段co代码去除了很多看起来稍稍复杂的细节,有利于捋清楚脉络。
 
看明白co库和compose库,你就知道,koajs只是给了大家用同步阻塞方式写IO处理的自由,实际上它在背后偷偷的采用了Promise链式调用的方式。
我刚开始了解koajs的时候,最大的疑问也正是这个:为什么一个异步非阻塞的语言可以用阻塞的方式处理异步的IO呢?
现在这个问题有了答案。
 
2015-11-16 于Evernote
 
 
 
原文地址:https://www.cnblogs.com/jasonxuli/p/5382090.html