Skip to content
js
// koa-compose

const app = {
  use(mid) {
    this.middleware.push(mid)
  },
  middleware: [],
}

app.use(async (ctx, next) => {
  console.log('mid1 enter')
  await next()
  // 利用 async / await 语法的特点,在中间件交出执行权后最终仍会回到这个的中间件,执行 await 后续的代码
  // Using the features of async/await syntax, after yielding control in the middleware, it will eventually return here to execute the code after await
  console.log('mid1 out')
})

app.use(async (ctx, next) => {
  console.log('mid2 enter')
  await asyncFunc(1000)
  await next()
  console.log('mid2 out')
})

app.use(async (ctx, next) => {
  console.log('mid3 enter')
  await asyncFunc(2000)
  await next()
  console.log('mid3 out')
})

function compose(middleware) {
  // 返回一个函数,等到需要的时候再执行
  // Return a function, to be executed when needed
  return function (ctx) {
    function dispatch(index) {
      if (index === middleware.length)
        return
      const mid = middleware[index]
      // koa 中间件中调用 next 方法,会交给下个中间件
      // In Koa middleware, calling the next method will pass control to the next middleware
      // 递归调用 dispatch
      // Recursively call dispatch
      // 因为中间件是一个 async 函数所以使用 Promise.resolve 包裹
      // Since middleware is an async function, wrap it with Promise.resolve
      return Promise.resolve(mid(ctx, () => dispatch(index + 1)))
    }
    dispatch(0)
  }
}

function asyncFunc(duration) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve()
    }, duration)
  })
}

const composedFunc = compose(app.middleware)
composedFunc({})