compose函数实现
- 每一个中间件需要封装一个
Promise - 洋葱模型的先进后出操作,对应
Promise.resolve的前后操作
const middleware1 = async (ctx, next) => { console.log(1); await next(); console.log(6);};const middleware2 = async (ctx, next) => { console.log(2); await next(); console.log(5);};const middleware3 = async (ctx, next) => { console.log(3); await next(); console.log(4);};const fn = compose([middleware1, middleware2, middleware3]);fn().then(() => { console.log("end");});function compose(middlewares) { if (!Array.isArray(middlewares)) { throw new TypeError("middle need a array"); } return function (ctx, next) { let index = -1; // 第一次调用 return dispatch(0); function dispatch(i) { if (i < index) { return Promise.reject(new Error("next() call multiple times")); } index = i; let fn = middlewares[i]; if (i === middlewares.length) { fn = next; } if (!fn) { return Promise.resolve(); } try { return Promise.resolve( fn(ctx, () => { return dispatch(i + 1); }) ); } catch (e) { return Promise.reject(e); } } };}// 结果显示// "1"// "2"// "3"// "4"// "5"// "6"// "end"
结合http与events模块实现
const http = require("http");const Emitter = require("events");const context = { _body: null, get body() { return this._body; }, set body(val) { this._body = val; this.res.end(this._body); },};function compose(middlewares) { if (!Array.isArray(middlewares)) { throw new TypeError("middle need a array"); } return function (ctx, next) { let index = -1; // 第一次调用 return dispatch(0); function dispatch(i) { if (i < index) { return Promise.reject(new Error("next() call multiple times")); } index = i; let fn = middlewares[i]; if (i === middlewares.length) { fn = next; } if (!fn) { return Promise.resolve(); } try { return Promise.resolve( fn(ctx, () => { return dispatch(i + 1); }) ); } catch (e) { return Promise.reject(e); } } };}class MyKoa extends Emitter { constructor() { super(); this.middleware = []; this.context = Object.create(context); } /** * 服务端监听 * @param {...any} args */ listen(...args) { const server = http.createServer(this.callback()); return server.listen(...args); } /** * 注册使用中间件 * @param {*} fn */ use(fn) { if (typeof fn === "function") { this.middleware.push(fn); } } /** * 中间件回调方法 */ callback() { let that = this; if (this.listeners("error").length === 0) { this.on("error", this.onerror); } const handleRequest = (req, res) => { let context = this.createContext(req, res); let middleware = this.middleware; compose(middleware)(context).catch((err) => this.onerror(err)); }; return handleRequest; } /** * 异常处理监听 * @param {EndOfStreamError} err */ onerror(err) { console.log(err); } /** * 创建通用上下文 * @param {Object} req * @param {Object} res */ createContext(req, res) { let context = Object.create(this.context); context.req = req; context.res = res; return context; }}const app = new MyKoa();const PORT = 3001;app.use(async (ctx) => { ctx.body = "<p>this is a body</p>";});app.listen(PORT, () => { console.log(`the web server is starting at port ${PORT}`);});