所以我们只需要改造一下 compose() 函数,使他支持异步函数就即可:
// compose_test.js // ... function compose(middlewares) { return function () { return dispatch(0) function dispatch(i) { let fn = middlewares[i] if (!fn) { return Promise.resolve() } return Promise.resolve( fn(function next() { return dispatch(i + 1) }) ) } } } const middlewares = [fn1, fn2, fn3] const finalFn = compose(middlewares) finalFn()运行结果:
fn1 fn2 fn3 fn3 end fn2 end fn1 end完美!!!
完善 Moa我们直接把刚才的异步合成代码移植到 moa.js 中, 由于 koa 中还需要用到 ctx 字段,所以我们还要对 compose() 方法进行一些改造才能使用:
// moa.js // ... class Moa { // ... compose(middlewares) { return function (ctx) { return dispatch(0) function dispatch(i) { let fn = middlewares[i] if (!fn) { return Promise.resolve() } return Promise.resolve( fn(ctx, function () { return dispatch(i + 1) }) ) } } } }实现完 compose() 方法之后我们继续完善我们的代码,首先我们需要给类在构造的时候,添加一个 middlewares,用来记录所有需要进行组合的函数,接着在use() 方法中把我们每一次调用的回调都记录一下,保存到middlewares 中,最后再在合适的地方调用即可:
// moa.js // ... class Moa { constructor() { this.middlewares = [] } use(middleware) { this.middlewares.push(middleware) } listen(...args) { const server = http.createServer(async (req, res) => { // 创建上下文 const ctx = this.createContext(req, res) const fn = this.compose(this.middlewares) await fn(ctx) // 响应 res.end(ctx.body) }) server.listen(...args) } // ... }我们加一小段代码测试一下:
// index.js //... const delay = () => new Promise(resolve => setTimeout(() => resolve() , 2000)) app.use(async (ctx, next) => { ctx.body = "1" await next() ctx.body += "5" }) app.use(async (ctx, next) => { ctx.body += "2" await delay() await next() ctx.body += "4" }) app.use(async (ctx, next) => { ctx.body += "3" })运行命令 node index.js 启动服务器后,我们访问页面 localhost:3000 查看一下,发现页面显示 12345 !
到此,我们简版的 Koa 就已经完成实现了。让我们庆祝一下先!!!
RouterKoa 还有一个很重要的路由功能,感觉缺少路由就缺少了他的完整性,所以我们简单介绍下如何实现路由功能。
其实,路由的原理就是根据地址和方法,调用相对应的函数即可,其核心就是要利用一张表,记录下注册的路由和方法,原理图如下所示:
使用方式如下:
// index.js // ... const Router = require('./router') const router = new Router() router.get('http://www.likecs.com/', async ctx => { ctx.body = 'index page' }) router.get('/home', async ctx => { ctx.body = 'home page' }) router.post('http://www.likecs.com/', async ctx => { ctx.body = 'post index' }) app.use(router.routes()) // ...我们来实现下 router 这个类,先在根目录创建一个 router.js 文件,然后根据路由的原理,我们实现下代码:
// router.js class Router { constructor() { this.stacks = [] } register(path, method, middleware) { this.stacks.push({ path, method, middleware }) } get(path, middleware) { this.register(path, 'get', middleware) } post(path, middleware) { this.register(path, 'post', middleware) } routes() { return async (ctx, next) => { let url = ctx.url === '/index' ? 'http://www.likecs.com/' : ctx.url let method = ctx.method let route for (let i = 0; i < this.stacks.length; i++) { let item = this.stacks[i] if (item.path === url && item.method === method) { route = item.middleware break } } if (typeof route === 'function') { await route(ctx, next) return } await next() } } } module.exports = Router启动服务器后,测试下 loacalhost:3000, 返回页面上 index page 表示路由实现成功!
本文源码地址: https://github.com/marklin2012/moa/
欢迎关注凹凸实验室博客:aotu.io
或者关注凹凸实验室公众号(AOTULabs),不定时推送文章: