对于使用过nodejs中koa2框架的同学应该很熟悉中间件是koa2的核心部分,接下来我们将中间件的实现尝试写一下.
在开发之前我先整理一下koa2的API,再通过这些API去反推它的实现:
const Koa2 = require("koa");
const app = new Koa2();
app.use(async (ctx, next) => {
console.log('111');
var result = await next();
console.log('222');
});
app.use(async (ctx, next) => {
console.log('333');
await next();
let testVar = await complicateCalc();
console.log('444');
});
app.use(async (ctx, next) => {
console.log('555');
ctx.res.end("hello world");
console.log('666');
});
function complicateCalc() {
return new Promise(function (resovle, reject) {
setTimeout(function () {
console.log("复杂计算中...");
resovle('5');
}, 5000)
});
}
app.listen(8000);
分析:koa2的最著名的就是洋葱模型,简而言之就是当前中间件函数会阻塞在next()函数执行的地方,只有当next函数内部所有的不管是同步还是异步的操作都执行完毕后才会回到当前函数继续去执行next下面的代码部分.所以上面代码输出的结果是:
最开始先输出111,遇到next函数进入到下一个中间件输出333,又遇到next函数进入下一个中间件输出555和666.此时向前回溯执行第二个中间件next下面的部分,结果遇到一个很强的异步操作函数complicateCalc,此时线程就会阻塞在这里等待计算完毕,计算完毕了之后就输出444.紧接着向第一层回溯最后输出222.整个运行流程就是上面所描述的,综合整个过程最难实现的部分就是这个next函数该如何编写.我们可以观察出next函数的特点有两个,第一是要在next函数中运行下一个中间件,第二就是我们如果想维持这种洋葱模型在next函数中必须要阻塞住下一个中间件的异步运行,等下一个中间件所有代码运行完毕后再返回一个Promise就可以了.
根据上面分析的特点尝试编写下面的脚本index.js
const http = require("http");
class Koa2 {
app = null;
funList = [];
constructor() {
this.app = http.createServer((req, res) => {
const ctx = {
req,
res
}
if (req.url !== "/favicon.ico") {
this.requestHandler(ctx);
}
})
}
/**
* 处理请求
*/
requestHandler(ctx) {
let fun_arr = [...this.funList];
const next = async () => {
const fn = fun_arr.shift();
if (fn) {
try {
await fn(ctx, next);
return Promise.resolve(null);
} catch (error) {
return Promise.reject(err);
}
}
}
next();
}
use(fn) {
this.funList.push(fn);
}
listen(port) {
this.app.listen(port);
}
}
module.exports = Koa2;
最后:将上面导出的模块给其他文件引入就可以编写支持洋葱模型的koa2中间件了.最后还要注意一点如果我们需要在中间件函数中编写异步的操作比如上面的complicateCalc函数,一定要在中间件函数内使用await阻塞住异步函数的执行,否则运行的顺序就不符合洋葱模型了.