Koa2源码学习
koa2.x
koa是继express之后的又一个主流的node服务器框架,由express的原班人马打造,相比express更加轻量,内部不包含任何的中间件,我先从最新的koa2.2看起。koa2.x使用了ex2016草案中的新特性async/await。语法更加简洁,语义更加明显。node v7.6.0+才支持async语法,低版本如果要使用还要安装babel
async/await
前端处理异步的发展经历了几个阶段,从当初的回调函数,到es2015的promise,到koa1的generator函数的使用,再到async/await的语法使用,目的都是想脱离回调地狱,使得异步回调的代码看起来像同步执行代码一样优雅。先给出async的使用小例子:
async函数返回的是一个promise函数。而promise状态的变化只有等到async内部流程执行完成。如果有await,而await之后又是一个promise对象,执行流程就会停在await这一步,等待异步promise返回。再执行下一步流程。从语义上确实清晰很多。
koa中间件
回到koa2.x上面,给个例子说下,如何编写中间件:
如果把中间件想作一个栈,请求会从顶部的第一个中间件开始处理,遇到next()调用,就会进入下一个中间件中,直到最后没有next调用,再从栈底反弹,一个一个执行之前next()之后的代码。这种实现方式可以大概猜测下,应该是将中间件存放在一个数组中进行一些操作。
koa源码
koa源码不长,也很简洁漂亮,先看package.json,”main”:”lib/application.js”表明入口就是application.js,这里简单截取了几个重要的代码片段
这里直接exports的就是Application类。我们使用var app = new koa(),可见app就是Application的实例。app.listen(3000)在koa中用来创建httpServer,实际和所有的node服务器框架一样,还是基于http.createServer的封装,这里的参数this.callback()作为参数正好来处理request和response。在callback中使用了一个compose对middleware数组进行组合,middleware中存放的正是使用app.use注册的一个个中间件,在use的api中,可以看到对generator的中间件注册方法进行了deprecate的提示,当然目前依然还是支持generator的写法,不用使用到了koa-convert进行转化,所以目前最好还是与时俱进使用await语法。
接着看compose内部代码,compose来源自koa-compose包:
这里compose直接返回了一个匿名函数延迟执行,这个函数在fn(ctx)的时候被执行,这里的ctx实际基于原型继承自this.context。context实际就是一个简单的对象,有一些简单的api:比如toJSON(),onerror();并且又通过delegate的方式将request和response中的方法代理到上面。先dispatch(0), Promise.resolve的用法实际是将现有对象转化为Promise对象,这个具体可以看下阮一峰的promise使用介绍:http://es6.ruanyifeng.com/#docs/promise#Promise-resolve。这里的fn刚好是async函数,返回值是promise对象,Promise.resolve会直接返回这个promise对象
以开始的app.js代码为例。fn(ctx)执行结果相当于:
代码执行到await会进一步执行下一个dispatch,如此递归回调,等到i === middleware.length时候,fn = next。fn调用一直没有传递next参数,所以next一直是undefined,直接被resolve。最终递归临界条件达到,回溯。所有中间件执行完成之后会调用handleResponse,也就是respond
在这个方法中进行发送response的操作,即res.end。