模型介绍
洋葱圈模型:所有的请求经过一个中间件的时候都会执行两次,对比Express形式的中间件,洋葱圈的模型可以非常方便的实现后置处理逻辑。 代表有egg、koa框架
代码执行顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const Koa = require('koa');
const app = new Koa(); const PORT = 3000;
// #1 app.use(async (ctx, next)=>{ console.log(1) await next(); console.log(1) }); // #2 app.use(async (ctx, next) => { console.log(2) await next(); console.log(2) })
app.use(async (ctx, next) => { console.log(3) })
app.listen(PORT); console.log(`http://localhost:${PORT}`);
// 打印结果 // 1 // 2 // 3 // 2 // 1
|
使用案例
原理实现
- 每个中间件都接收了一个next参数,在next函数运行之前的中间件代码会在一开始就执行,next函数之后的代码会在内部的中间件全部运行结束之后才执行。
- 实现中间件的步骤
- 首先我们要知道当前中间件的数组集合
- 然后构建一个组合方法,对这些中间件按照洋葱的结构进行组合,并执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| // middleware用来保存中间件 app.use = (fn) => { this.middleware.push(fn) return this }
// compose组合函数来规定执行次序 function compose (middleware) { // context:上下文,next:传入的接下来要运行的函数 return function (context, next) { function dispatch (i) { index = i // 中间件 let fn = middleware[i] if (!fn) return Promise.resolve() try { // 我们这边假设和上文中的例子一样,有A、B、C三个中间件 // 通过dispatch(0)发起了第一个中间件A的执行 // A中间件执行之后,next作为dispatch(1)会被执行 // 从而发起了下一个中间件B的执行,然后是中间件C被执行 // 所有的中间件都执行了一遍后,执行Promise.resolve() // 最里面的中间件C的await next()运行结束,会继续执行console.log("C2") // 整个中间件C的运行结束又触发了Promise.resolve // 中间件B开始执行console.log("B2") // 同理,中间件A执行console.log("A2") return Promise.resolve(fn(context, () => { return dispatch(i + 1) })) } catch (err) { return Promise.reject(err) } } return dispatch(0) } }
|
文章来源参考