本文地址 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. 支持向量机(SVM)非线性数据切割

    支持向量机(SVM)非线性数据切割 1.目标 本指导中你将学到: l  当不可能线性切割训练数据时,如何定义SVM最优化问题. l  在这样的问题上.如何配置CvSVMParams中的參数满足你的SV ...

  2. android广播集合,intent,action

    android.permission.ACCESS_CHECKIN_PROPERTIES 同意读写訪问"properties"表在checkin数据库中,改值能够改动上传( All ...

  3. SSH框架之Struts(2)——Struts的执行流程之配置文件

    上篇我们大致了解了一下採用了Struts框架的web页面运行流程. 接下来的几篇我们通过Struts的源代码来学习一下Struts的内部原理. 当server启动的时候.server会依据配置文件初始 ...

  4. 三大文本处理工具grep、sed及awk的简单介绍

    grep.sed和awk都是文本处理工具,虽然都是文本处理工具单却都有各自的优缺点,一种文本处理命令是不能被另一个完全替换的,否则也不会出现三个文本处理命令了.只不过,相比较而言,sed和awk功能更 ...

  5. day01 Java基础

    1.Win7中,在某目录下:shift+右键->在当前目录打开命令行窗口. Windows中打开画图工具的命令是:mspaint. 2.Windows DOS下“rd *”是删除目录的命令:“r ...

  6. Pass value from child popup window to parent page window using JavaScript--reference

    Here Mudassar Ahmed Khan has explained how to pass value from child popup window to parent page wind ...

  7. 关于struts2的modelDriven

    今天做毕业设计,前台往后台赋值,习惯性的用了modelDriven.但是刚写完就奇怪它的机理是怎样的,它怎么知道我前台传的参是哪个Model的属性(之前用servlet都是手动),于是手贱的ctrl点 ...

  8. html禁止清除input文本输入缓存

    多数浏览器默认会缓存input的值,只有使用ctl+F5强制刷新的才可以清除缓存记录. 如果不想让浏览器缓存input的值,有2种方法: 方法一: 在不想使用缓存的input中添加 autocompl ...

  9. PHP读书笔记(3)-常量

    什么是常量 什么是常量?常量可以理解为值不变的量 :或者是常量值被定义后,在脚本的其他任何地方都不可以被改变.手册上是这么定义PHP的常量:常量是一个简单值的标识符(名字).如同其名称所暗示的,在脚本 ...

  10. python(6)-类

    面向对象编程是一种编程方式,此编程方式的落地需要使用 "类" 和 "对象" 来实现,所以,面向对象编程其实就是对 "类" 和 "对 ...