您的位置:首页 > Web前端 > JavaScript

Koajs原理

2016-04-12 11:50 435 查看
本文地址 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: