下面就是模拟koa中间件的代码,所谓形象的洋葱圈
const Koa = require('koa'); const app = new Koa(); // 第一层包含第二层 第二程包含第三层 // logger app.use(async (ctx, next) => { console.log('第一层洋葱 - 开始') await next(); const rt = ctx.response.get('X-Response-Time'); console.log(`${ctx.method} ${ctx.url} - ${rt}`); console.log('第一层洋葱 - 结束') }); // x-response-time app.use(async (ctx, next) => { console.log('第二层洋葱 - 开始') const start = Date.now(); await next(); const ms = Date.now() - start; ctx.set('X-Response-Time', `${ms}ms`); console.log('第二层洋葱 - 结束') }); // response app.use(async ctx => { console.log('第三层洋葱 - 开始') ctx.body = 'Hello World'; console.log('第三层洋葱 - 结束') }); app.listen(8000);第一层包含第二层 第二程包含第三层,所以才会有下面的执行结果
第一层洋葱 - 开始 第二层洋葱 - 开始 第三层洋葱 - 开始 第三层洋葱 - 结束 第二层洋葱 - 结束 GET / - 6ms 第一层洋葱 - 结束 第一层洋葱 - 开始 第二层洋葱 - 开始 第三层洋葱 - 开始 第三层洋葱 - 结束 第二层洋葱 - 结束 GET /favicon.ico - 1ms 第一层洋葱 - 结束koa 中间件简单代码实现
const http = require('http') // 组合中间件 function compose(middlewareList) { return function (ctx) { function dispatch(i) { const fn = middlewareList[i] try { return Promise.resolve( fn(ctx, dispatch.bind(null, i + 1)) // promise ) } catch (err) { return Promise.reject(err) } } return dispatch(0) } } class LikeKoa2 { constructor() { this.middlewareList = [] } use(fn) { this.middlewareList.push(fn) return this } createContext(req, res) { const ctx = { req, res } ctx.query = req.query return ctx } handleRequest(ctx, fn) { return fn(ctx) } callback() { const fn = compose(this.middlewareList) return (req, res) => { const ctx = this.createContext(req, res) return this.handleRequest(ctx, fn) } } listen(...args) { const server = http.createServer(this.callback()) server.listen(...args) } } module.exports = LikeKoa2