本文地址 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
 
 
 

Koajs原理的更多相关文章

  1. 【Node.js】 bodyparser实现原理解析

    为什么我们需要body-parser 也许你第一次和bodyparser相遇是在使用Koa框架的时候.当我们尝试从一个浏览器发来的POST请求中取得请求报文实体的时候,这个时候,我们想,这个从Koa自 ...

  2. 手写koa-static源码,深入理解静态服务器原理

    这篇文章继续前面的Koa源码系列,这个系列已经有两篇文章了: 第一篇讲解了Koa的核心架构和源码:手写Koa.js源码 第二篇讲解了@koa/router的架构和源码:手写@koa/router源码 ...

  3. 奇异值分解(SVD)原理与在降维中的应用

    奇异值分解(Singular Value Decomposition,以下简称SVD)是在机器学习领域广泛应用的算法,它不光可以用于降维算法中的特征分解,还可以用于推荐系统,以及自然语言处理等领域.是 ...

  4. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  5. 线性判别分析LDA原理总结

    在主成分分析(PCA)原理总结中,我们对降维算法PCA做了总结.这里我们就对另外一种经典的降维方法线性判别分析(Linear Discriminant Analysis, 以下简称LDA)做一个总结. ...

  6. [原] KVM 虚拟化原理探究(1)— overview

    KVM 虚拟化原理探究- overview 标签(空格分隔): KVM 写在前面的话 本文不介绍kvm和qemu的基本安装操作,希望读者具有一定的KVM实践经验.同时希望借此系列博客,能够对KVM底层 ...

  7. H5单页面手势滑屏切换原理

    H5单页面手势滑屏切换是采用HTML5 触摸事件(Touch) 和 CSS3动画(Transform,Transition)来实现的,效果图如下所示,本文简单说一下其实现原理和主要思路. 1.实现原理 ...

  8. .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理

    .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...

  9. python自动化测试(2)-自动化基本技术原理

    python自动化测试(2) 自动化基本技术原理 1   概述 在之前的文章里面提到过:做自动化的首要本领就是要会 透过现象看本质 ,落实到实际的IT工作中就是 透过界面看数据. 掌握上面的这样的本领 ...

随机推荐

  1. iis下FastCGI 的常见Error错误

    用iis服务器+FastCGI配置的php环境会经常出现FastCGI Error的错误,像5 (0x80070005).2147467259(0x80004005).1413 (0x80070585 ...

  2. PI-webservice06-调用外部webservice过程中注意问题

    1,SAP与.NET系统之间通过webservice来进行数据交互的过程中,格式是有要求的,要求.NET发布出来的webservice中的数据是用list来进行传输的,不能用datatable和lis ...

  3. 暂停和恢复Activity Android

    暂停和恢复Activity(Pausing and Resuming an Activity) 在正常的应用程序使用,前台activity有时会被其他可视化组件遮挡,从而 造成activity的暂停. ...

  4. PAT 1020

    1020. Tree Traversals (25) Suppose that all the keys in a binary tree are distinct positive integers ...

  5. google maps api申请的问题

    现在已经改由统一的GOOGLE API控制台进行所有GOOGLE API的管理了. 方法是使用Google帐号登入 https://code.google.com/apis/console. 然后在所 ...

  6. Hidden Markov Model

    Markov Chain 马尔科夫链(Markov chain)是一个具有马氏性的随机过程,其时间和状态参数都是离散的.马尔科夫链可用于描述系统在状态空间中的各种状态之间的转移情况,其中下一个状态仅依 ...

  7. 用CAS操作实现Go标准库中的Once

    Go标准库中提供了Sync.Once来实现"只执行一次"的功能.学习了一下源代码,里面用的是经典的双重检查的模式: // Once is an object that will p ...

  8. php中var_export与var_dump的区别分析

    一 var_dump (PHP 3 >= 3.0.5, PHP 4, PHP 5) var_dump -- 打印变量的相关信息 描述 void var_dump ( mixed expressi ...

  9. oracle时间格式转换问题 ORA-01810: format code appears twice--转

    今天在做报表查询的时候Oracle报错: 信息为 ORA-01810: format code appears twice 原因:由于想java一样转化时间格式,但是Oracle中是不区分大小写的,所 ...

  10. MII接口全家福

    转载:http://blog.chinaunix.net/uid-24148050-id-131084.html 简介:    MII是英文Medium Independent Interface的缩 ...