洋葱模型 compose方法

如果中间件场景中不存在前后依赖的情况,从头到尾按顺序链式调用完全没有问题,但如果存在依赖情况呢? 如果只链式执行一次,无法保证前面的中间件能使用之后中间件添加的东西。

同步版本

  1. class Koa {
  2. constructor() {
  3. this.middlewares = []
  4. }
  5. use (fn) {
  6. this.middlewares.push(fn)
  7. }
  8. compose () {
  9. function dispatch(index) {
  10. // 如果所有中间件都执行完跳出
  11. if (index === app.middlewares.length) return
  12. // 取出第 index 个中间件并执行
  13. const fn = app.middlewares[index]
  14. // () => dispatch(index + 1),这其实就是next方法
  15. return fn(() => dispatch(index + 1))
  16. }
  17. dispatch(0);
  18. }
  19. }
  20. // 调用
  21. const app = new Koa()
  22. app.use((next) => {
  23. console.log(1)
  24. next()
  25. console.log(2)
  26. })
  27. app.use((next) => {
  28. console.log(3)
  29. next()
  30. console.log(4)
  31. })
  32. app.use((next) => {
  33. console.log(5)
  34. next()
  35. console.log(6)
  36. })
  37. app.compose()

由于下一个中间件,作为上一个中间件的next参数传入,使得上面的代码输出为 1,3,5,6,4,2

异步版本

正常情况下,如果中间件为异步(涉及到数据库读取操作,请求远程资源的),我们会用 await next()
需要修改下同步版本,使同步版本返回一个 Promise 即可,如下

  1. class Koa {
  2. constructor() {
  3. this.middlewares = []
  4. }
  5. use (fn) {
  6. this.middlewares.push(fn)
  7. }
  8. compose () {
  9. function dispatch(index) {
  10. // 如果所有中间件都执行完跳出
  11. if (index === app.middlewares.length) return Promise.resolve()
  12. // 取出第 index 个中间件并执行
  13. const fn = app.middlewares[index]
  14. // () => dispatch(index + 1),这其实就是next方法
  15. return Promise.resolve(fn(() => dispatch(index + 1)))
  16. }
  17. dispatch(0)
  18. }
  19. }
  20. const app = new Koa()
  21. app.use(async (next) => {
  22. console.log(1)
  23. await next()
  24. console.log(2)
  25. })
  26. app.use(async (next) => {
  27. console.log(3)
  28. await next()
  29. console.log(4)
  30. })
  31. app.use(async (next) => {
  32. console.log(5)
  33. await next()
  34. console.log(6)
  35. })
  36. app.compose()