阅读原文
前言
Koa
是当下主流 NodeJS 框架,以轻量见长,而它中间件机制与相对传统的 Express
支持了异步,所以编码时经常使用 async/await
,提高了可读性,使代码变得更优雅,上一篇文章 NodeJS 进阶 —— Koa 源码分析,也对 “洋葱模型” 和实现它的 compose
进行分析,由于个人觉得 compose
的编程思想比较重要,应用广泛,所以本篇借着 “洋葱模型” 的话题,打算用四种方式来实现 compose
。
洋葱模型案例
如果你已经使用 Koa
对 “洋葱模型” 这个词一定不陌生,它就是 Koa
中间件的一种串行机制,并且是支持异步的,下面是一个表达 “洋葱模型” 的经典案例。
const Koa = require("koa");
const app = new Koa();
app.use(asycn (ctx, next) => {
console.log(1);
await next();
console.log(2);
});
app.use(asycn (ctx, next) => {
console.log(3);
await next();
console.log(4);
});
app.use(asycn (ctx, next) => {
console.log(5);
await next();
console.log(6);
});
app.listen(3000);
// 1
// 3
// 5
// 6
// 4
// 2
上面的写法我们按照官方推荐,使用了 async/await
,但如果是同步代码不使用也没有关系,这里简单的分析一下执行机制,第一个中间件函数中如果执行了 next
,则下一个中间件会被执行,依次类推,就有了我们上面的结果,而在 Koa
源码中,这一功能是靠一个 compose
方法实现的,我们本文四种实现 compose
的方式中实现同步和异步,并附带对应的案例来验证。
准备工作
在真正创建 compose
方法之前应该先做些准备工作,比如创建一个 app
对象来顶替 Koa
创建出的实例对象,并添加 use
方法和管理中间件的数组 middlewares
。
// 文件:app.js
// 模拟 Koa 创建的实例
const app = {
middlewares: []
};
// 创建 use 方法
app.use = function(fn) {
app.middlewares.push(fn);
};
// app.compose.....
module.exports = app;
上面的模块中导出了 app
对象,并创建了存储中间件函数的 middlewares
和添加中间件的 use
方法,因为无论用哪种方式实现 compose
这些都是需要的,只是 compose
逻辑的不同,所以后面的代码块中会只写 compose
方法。
Koa 中 compose 的实现方式
首先介绍的是 Koa
源码中的实现方式,在 Koa
源码中其实是通过 koa-compose
中间件来实现的,我们在这里将这个模块的核心逻辑抽取出来,用我们自己的方式实现,由于重点在于分析 compose
的原理,所以 ctx
参数就被去掉了,因为我们不会使用它,重点是 next
参数。
1、同步的实现
// 文件:app.js
app.compose = function() {
// 递归函数
function dispatch(index) {
// 如果所有中间件都执行完跳出
if (index === app.<