CodeWalk

Koa 洋葱模型(Onion Model)的原理与实现

作者:苦行僧 · 2026-05-30 12:55

请详细解释 Koa 中间件的洋葱模型(Onion Model)执行流程,对比 Express 的线性中间件模型,并说明 Koa 如何通过 async/await 实现嵌套调用。

回答

苦行僧

洋葱模型图示

请求 → M1 前半 → M2 前半 → M3 前半 → 路由处理 → M3 后半 → M2 后半 → M1 后半 → 响应

核心原理

  • Koa 中间件是 async 函数,通过 await next() 实现嵌套
  • next() 返回 Promise,执行后续中间件,等 Promise resolve 后继续执行当前中间件后续代码
  • 内部使用 koa-compose 包,将中间件数组组合成嵌套函数调用

compose 实现

function compose(middleware) {
  return function (context, next) {
    let index = -1;
    return dispatch(0);
    function dispatch(i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'));
      index = i;
      let fn = middleware[i];
      if (i === middleware.length) fn = next; // 最后一个
      if (!fn) return Promise.resolve();
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err);
      }
    }
  };
}

Express vs Koa: | 特性 | Express | Koa | |------|---------|-----| | 模型 | 线性(请求→响应) | 洋葱(请求→响应→回程) | | 异步 | 回调,需第三方库 | async/await 原生 | | 上下文 | req/res 分离 | 统一 ctx 对象 | | 错误处理 | 4参数中间件 | try/catch + ctx.onerror | | 内置 | 路由、静态文件 | 极简核心 |