中间件原理

如果使用过koa、egg这两个Node框架,请简述其中的中间件原理,最好用代码表示一下
Egg Koa - 图1
上面是在网上找的一个示意图,就是说中间件执行就像洋葱一样,最早use的中间件,就放在最外层。处理顺序从左到右,左边接收一个request,右边输出返回response

一般的中间件都会执行两次,调用next之前为第一次,调用next时把控制传递给下游的下一个中间件。当下游不再有中间件或者没有执行next函数时,就将依次恢复上游中间件的行为,让上游中间件执行next之后的代码

例如下面这段代码:

  1. const Koa = require('koa')
  2. const app = new Koa()
  3. app.use((ctx, next) => {
  4. console.log(1)
  5. next()
  6. console.log(3)
  7. })
  8. app.use((ctx) => {
  9. console.log(2)
  10. })
  11. app.listen(3001)
  12. 执行结果是1=>2=>3

koa中间件实现源码大致思路如下:

  1. // 注意其中的compose函数,这个函数是实现中间件洋葱模型的关键
  2. // 场景模拟
  3. // 异步 promise 模拟
  4. const delay = async () => {
  5. return new Promise((resolve, reject) => {
  6. setTimeout(() => {
  7. resolve();
  8. }, 2000);
  9. });
  10. }
  11. // 中间间模拟
  12. const fn1 = async (ctx, next) => {
  13. console.log(1);
  14. await next();
  15. console.log(2);
  16. }
  17. const fn2 = async (ctx, next) => {
  18. console.log(3);
  19. await delay();
  20. await next();
  21. console.log(4);
  22. }
  23. const fn3 = async (ctx, next) => {
  24. console.log(5);
  25. }
  26. const middlewares = [fn1, fn2, fn3];
  27. // compose 实现洋葱模型
  28. const compose = (middlewares, ctx) => {
  29. const dispatch = (i) => {
  30. let fn = middlewares[i];
  31. if(!fn){ return Promise.resolve() }
  32. return Promise.resolve(fn(ctx, () => {
  33. return dispatch(i+1);
  34. }));
  35. }
  36. return dispatch(0);
  37. }