koa中间件之compose

461 阅读2分钟

koa

koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。

核心-compose

koa本身相较于其他web框架来说更轻量。但是如果要实现更健壮的服务,我们就需要添加大量的中间件来丰富我们的服务! 所以对比其他web框架,koa有自己独特的处理中间件的方式-->compose!

源码分析

compose的源码并不多,二十几行,那它是如何实现大家所认为的洋葱模型

需求

探索源码前我们先定义下当前的场景,以便于更好的描述

const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
    console.log(1);
    await next();
    console.log(1.1)
})
app.use(async (ctx, next) => {
    console.log(2)
    await next();
    console.log(2.1)
})
app.use(async (ctx, next) => {
    console.log(3)
    await next();
    console.log(3.1)
})
app.listen(8080, () => {
    console.log('8080')
})
// 输出结果 1-->2-->3-->3.1-->2.1-->1.1
源码

源码地址

// 这是我简化一部分异常处理后的源码
// middleware 为存放中间件函数的数组
function compose (middleware) {
  return function (context, next) {
    let index = -1
    return dispatch(0)
    
    function dispatch (i) {
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      return Promise.resolve(fn(context, function next () {
          return dispatch(i + 1)
      }))
    }
  }
}

分析调用的过程:

  • 1、取出middleware中的第一个中间件函数并且用Promise.resolve包装一下执行,执行的时候传入context上下文和next函数
  • 2、next的函数体内容实际就是调用第二个被Promise.resolve包装后的中间件函数,所以,当定义的中间函数调用next()方法的时候实际是去调用第二个中间件函数执行了,并且我们发现,所有的中间件函数都是被包装在Promise.resolve中执行;所以我们可以在中间件函数中 await next()
  • 3、当middle中最后一个中间件函数调用完毕后,根据代码从上往下执行的逻辑,会依次往前执行其他中间件next后边的代码
简单版实现compose

我这里实现了一个简单版的中间件处理逻辑,根据这个帮我们更好的去理解compose

function compose(middleware) {
    dispatch(0);
    function dispatch(idx) {
        middleware[idx](ctx, () => dispatch(idx + 1));
    }
}

结论

听过上述的分析,相信大家对Koa的中间件处理有了一定的认识;看起来Koa的中间件处理就是一个洋葱模型,但是在某种情况下,可能这个洋葱模型是不完整,因为有些时候程序不会走完整个中间件的处理,可能在第一个或中间某个中间就已经结束了。