The previous section realized the context, and the previous section used a bit of middleware. This section implements the onion model of koa middleware.
Ideas:
- Store all user callbacks
- Combine all the callbacks passed by the user (compose in redux)
- Combine into a linear structure and execute in sequence, and return a large promise after the combination
- When the combined promise is completed, get the final result and respond back
application.js
The code implementation is as follows: the core is the implementation of the combination method
const EventEmitter = require("events");
const http = require("http");
const context = require("./context");
const request = require("./request");
const response = require("./response");
console.log("kaimo-koa---->");
class Application extends EventEmitter {
constructor() {
super();
// 防止多个实例共享 context request response 需要进行拷贝
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
// 储存用户所有的 callback
this.middlewares = [];
}
use(callback) {
// 将用户传递的 callback 全部组合起来
this.middlewares.push(callback);
}
// 创建一个上下文
createContext(req, res) {
// 每次请求都应该是一个全新的 context,需要拷贝
let ctx = Object.create(this.context);
// 上下文中有一个 request 对象,是自己封装的
ctx.request = Object.create(this.request);
// 上下文中还有一个 req 属性 指代的是原生的 req,自己封装的 request 对象上有 req 属性
ctx.req = ctx.request.req = req;
// 上下文中还有一个 response 对象,是自己封装的
ctx.response = Object.create(this.response);
// 上下文中还有一个 res 属性 指代的是原生的 res,自己封装的 response 对象上有 res 属性
ctx.res = ctx.response.res = res;
return ctx;
}
compose(ctx) {
// 在数组中取出第一个,第一个执行后执行第二个
const dispatch = (i) => {
if (i === this.middlewares.length) return Promise.resolve();
let middleware = this.middlewares[i];
// 中间件如果不是 async 需要 Promise 包装一下,() => dispatch(i + 1) 就是 next
return Promise.resolve(middleware(ctx, () => dispatch(i + 1)));
};
return dispatch(0);
}
async handleRequest(req, res) {
const ctx = this.createContext(req, res);
// 组合成一个线性结构依次执行,组合完返回一个大的 promise
await this.compose(ctx);
// 当组合后的 promise 完成后,拿到最终的结果响应回去
let body = ctx.body;
res.end(body);
}
listen(...args) {
const server = http.createServer(this.handleRequest.bind(this));
server.listen(...args);
}
}
module.exports = Application;
Then we write the testdemo.js
const Koa = require("./kaimo-koa");
const app = new Koa();
const log = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("kaimo313");
resolve();
}, 3000);
});
};
app.use(async (ctx, next) => {
console.log(1);
console.time("kaimo");
await next();
ctx.body = "hello 1";
console.log(2);
console.timeEnd("kaimo");
});
app.use(async (ctx, next) => {
console.log(3);
await log();
ctx.body = "hello 2";
await next();
console.log(4);
});
app.use(async (ctx, next) => {
console.log(5);
ctx.body = "hello 3";
await next();
console.log(6);
});
app.listen(3000);
Start the service, accesshttp://localhost:3000/
nodemon demo.js