侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现一个中间件执行流程的 compose 函数

2025-12-7 / 0 评论 / 4 阅读

题目

实现一个中间件执行流程的 compose 函数

信息

  • 类型:问答
  • 难度:⭐⭐

考点

中间件原理, 洋葱模型, 异步流程控制

快速回答

实现要点:

  • 使用递归或迭代处理中间件队列
  • 每个中间件接收 contextnext 参数
  • 通过 dispatch 函数控制执行流程
  • 支持异步中间件(返回 Promise)
## 解析

原理说明

中间件核心是洋葱模型(Onion Model),请求从外到内穿透中间件栈,响应从内到外返回。关键是通过 compose 函数将多个中间件组合成嵌套结构:

middleware1(ctx, () => middleware2(ctx, () => middleware3(ctx, next)))

代码实现

递归版 compose 函数:

function compose(middlewares) {
  return function (ctx, next) {
    let index = -1

    function dispatch(i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i

      let fn = middlewares[i]
      if (i === middlewares.length) fn = next
      if (!fn) return Promise.resolve()

      try {
        return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
      } catch (err) {
        return Promise.reject(err)
      }
    }

    return dispatch(0)
  }
}

使用示例

// 定义中间件
const m1 = async (ctx, next) => {
  console.log('m1 start')
  await next()
  console.log('m1 end')
}

const m2 = async (ctx, next) => {
  console.log('m2 start')
  await next()
  console.log('m2 end')
}

// 组合执行
const fn = compose([m1, m2])
fn({}).then(() => {
  console.log('执行完成')
})

/* 输出顺序:
  m1 start
  m2 start
  m2 end
  m1 end
  执行完成
*/

最佳实践

  • 错误处理:在最外层添加 catch 中间件
  • 性能优化:迭代实现避免递归调用栈溢出
  • 上下文设计:通过 ctx 对象共享请求/响应数据

常见错误

  • 忘记调用 next() 导致后续中间件不执行
  • 多次调用 next() 引发执行顺序错乱
  • 未正确处理异步操作(缺少 async/await)

扩展知识

  • Koa 中间件原理:基于 compose 库实现,支持洋葱模型
  • Express 差异:线性执行(非洋葱模型),通过 next 手动控制
  • 高级应用:中间件可用于日志、鉴权、数据校验等横切关注点