本文地址 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. esxi 5.1 由于断电错误无法启动 报错 bank5 invalid configuration

    由于着急,处理过程中也没有截图,这里简单的描写叙述下整个过程吧. IBM pcserver x3850 可能是机器太热的原因,中午无故掉电,导致esxi无法正常启动 启动时报错 bank5 inval ...

  2. 使用IIS 7.0 Smooth Streaming 优化视频服务

    http://www.cnblogs.com/dudu/archive/2013/06/08/iis_webserver_settings.html (支持高并发的IIS Web服务器常用设置) ht ...

  3. Metadata Lock原理7

    http://blog.itpub.net/22664653/viewspace-1791744/ 一 简介 通过前面两篇文章的介绍,相信读到这里的各位对MDL 锁已经有了比较深入的了解了,本文将结合 ...

  4. SQL SERVER 中 GO 的用法2

    具体不废话了,请看下文详解. 1 2 3 4 5 6 7 8 9 10 use db_CSharp go  select *,  备注=case  when Grade>=90 then '成绩 ...

  5. PHP.5-DIV+CSS布局网站首页实例

    DIV+CSS布局网站首页实例 网站页面布局 http://www.sj33.cn/digital/wyll/201501/42379.html[页头.页脚.侧边栏和内容区域] #避免各浏览器对CSS ...

  6. python邮件发送接收

    接收邮件 import poplib,pdb,email,re,time from email import header POP_ADDR = r'pop.126.com' USER = '' PA ...

  7. request 和response 中的setCharacterEncoding区别

    response和request的setCharacterEncoding 一.request.setCharacterEncoding():是设置从request中取得的值或从数据库中取出的值. 指 ...

  8. iOS下编译ffmpeg

    网络上搜索“ios ffmpeg 编译”,文章一大把,但我编译还是费了很大的功夫才编译成功.很多文章只是把步骤列了出来,但是每个人的系统环境,或者程序版本都不一样,结果出现各种的错误.我把自己编译过程 ...

  9. windows下nginx+fastcgi不能使用file_get_contents/curl/fopen的原因

    这两天一直在搞windows下nginx+fastcgi的file_get_contents请求.我想,很多同学都遇到当file_get_contents请求外网的http/https的php文件时毫 ...

  10. 让footer固定在页面(视口)底部(CSS-Sticky-Footer)

    让footer固定在页面(视口)底部(CSS-Sticky-Footer) 这是一个让网站footer固定在浏览器(页面内容小于浏览器高度时)/页面底部的技巧.由HTML和CSS实现,没有令人讨厌的h ...