Koajs原理

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

时间: 2024-11-05 16:08:33

Koajs原理的相关文章

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

为什么我们需要body-parser 也许你第一次和bodyparser相遇是在使用Koa框架的时候.当我们尝试从一个浏览器发来的POST请求中取得请求报文实体的时候,这个时候,我们想,这个从Koa自带的ctx.body里面取出来就可以了嘛! 唉!等等,但根据Koa文档,ctx.body等同于ctx.res.body,所以从ctx.body取出来的是空的响应报文,而不是请求报文的实体哦 于是这时候又打算从Node文档里找找request对象有没有可以提供查询请求报文的属性,结果自然是Node文档

JVM原理讲解和调优

一.什么是JVM JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的. Java语言的一个非常重要的特点就是与平台的无关性.而使用Java虚拟机是实现这一特点的关键.一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码.而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译.Java语言使用Java虚拟机屏蔽了与具体平台相关的信息

小米手环 / 运动手环 记步功能原理

很多朋友是第一次接触像小米手环这类运动计步产品,对于那么轻盈小巧的手环能够精准计步,甚至能详细完整的记录睡眠时间觉得非常神奇,本文就和大家详细说说在看不见的小米手环背板下,它是怎么工作的. 1. 手机上的运动步数是怎么来的? A:简单来说:小米手环能够精准计步由硬件和软件算法两方面组成,缺一不可. 硬件 是指小米手环里内置的那枚强悍的三轴加速度传感器ADXL362 (注1),军用级,大家知道想要达到军用级,这得有多苛刻.其实三轴加速度传感器不神秘,在大多数中高档手机里都有配备加速度传感器,只是在

Nginx为什么比Apache Httpd高效:原理篇

一.进程.线程? 进程是具有一定独立功能的,在计算机中已经运行的程序的实体.在早期系统中(如linux 2.4以前),进程是基本运作单位,在支持线程的系统中(如windows,linux2.6)中,线程才是基本的运作单位,而进程只是线程的容器.程序 本身只是指令.数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例.若干进程有可能与同一个程序相关系,且每个进程皆可以同步(循 序)或异步(平行)的方式独立运行.现代计算机系统可在同一段时间内以进程的形式将多个程序加载到存储器中,并借

Jsp通过Filter实现UrlRewriter原理

web.xml文件: 1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=&q

MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory

从上文<MyBatis框架中Mapper映射配置的使用及原理解析(六) MapperRegistry> 中我们知道DefaultSqlSession的getMapper方法,最后是通过MapperRegistry对象获得Mapper实例: public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory =

Android分包MultiDex原理详解

MultiDex的产生背景 当Android系统安装一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt.DexOpt的执行过程是在第一次加载Dex文件的时候执行的.这个过程会生成一个ODEX文件,即Optimised Dex.执行ODex的效率会比直接执行Dex文件的效率要高很多. 但是在早期的Android系统中,DexOpt有一个问题,DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面.但是这个链表的长度是用一个short类型来保存的,导致

状态检测防火墙原理

状态检测防火墙原理 防火墙发展到今天,虽然不断有新的技术产生,但从网络协议分层的角度,仍然可以归为以下三类: 1.包过滤防火墙: 2.基于状态检测技术(Stateful-inspection)的防火墙: 3.应用层防火墙 这三类防火墙都是向前兼容的,即基于状态检测的防火墙也有一般包过滤防火墙的功能,而基于应用层的防火墙也包括前两种防火墙的功能.由于<<浅>>文已讲了第一类防火墙,在这里我就讲讲基于状态检测技术的防火墙的实现原理. 为什么会有基于状态检测的防火墙呢?这就要先看看第一类

JS 实现无缝滚动动画原理(初学者入)

这段时间在教培训班的学生使用原生javascript实现无缝滚动的动画案例,做了这个原理演示的动画,分享给自学JS的朋友!博主希望对你们有帮助! 在讲解之前先看一下demo: demo:https://224137748.github.io/JS_warehouse/lunbo/domo.HTML源码:https://github.com/224137748/JS_warehouse/blob/master/lunbo/domo.HTML ps: 上面和下面的滚动进度是一致的,上面红色框是为了演