express和koa中,中间件的执行过程

74 阅读2分钟

笔者最近读了一些express 和koa 的源码,发现:

  • 其实并非只有koa 实现了洋葱模型
  • 实际上express 也有洋葱模型的概念,但在执行异步函数next 的时候,express很难在前面触发的中间件中拿到结果
  • 但他俩有什么不同呢?这就是我接下来分析出来的结果
  • 话不多说,上才艺:
  1. 模仿express next 执行流程,粗略写了一下express中 next 函数的执行过程
// fn1、fn2、fn3 皆为中间件
async function fn1(ctx, next) {
  console.log('fn1 start')
  ctx.message += 'aaa'
  await next()
  console.log('fn1 end', ctx.message)
}
async function fn2(ctx, next) {
  console.log('fn2 start')
  ctx.message += 'bbb'
  await next()
  console.log('fn2 end')
}
async function fn3(ctx, next) {
  console.log('fn3 start')
  const res = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('ccc')
    }, 2000)
  })
  ctx.message += res
  console.log('fn3 end')
}

const middlewares = [fn1, fn2, fn3]
const context = {
  message: ''
}

// express 会将每个中间件都封装成一个layer 函数,这里只是模拟执行过程,所以简化版代码如下
let index = 0
next()
function next() {
  if (index > middlewares.length) return
  const fn = middlewares[index]
  index++
  fn(context, next)
}
  1. 模仿koa 的next 函数执行过程
 async function fn1(ctx, next) {
  console.log('fn1 start')
  ctx.message += 'aaa'
  await next()
  console.log('fn1 end', ctx.message)

}
async function fn2(ctx, next) {
  console.log('fn2 start')
  ctx.message += 'bbb'
  await next()
  console.log('fn2 end')
}
async function fn3(ctx, next) {
  console.log('fn3 start')
  const res = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('ccc')
    }, 2000)
  })
  ctx.message += res
  console.log('fn3 end')
}

// 模拟context数据,koa 会内部封装这个对象,此处不需要考虑那么复杂
const context = {
  message: ''
}
// koa 处理中间件的时候很简单,只是将其扔到内部的 middlewares 数组中
const middlewares = [fn1, fn2, fn3]

let index = 0

// 框架会帮助调用第一个中间件
dispatch(0)
function dispatch(index) {
  if (index > middlewares.length) return
  
  const fn = middlewares[index]
  
  // 这俩是执行结果处理函数,可以忽略
  const onSuccess = res => console.log('success')
  const onError = err => console.log('error')
  
  // 这里是重点!!!,返回的是promise
  return Promise.resolve(
      fn(context, dispatch.bind(null, ++index))
    ).then(onSuccess).catch(onError)
}

  • 对比之下,不难发现,express中的next函数并没有返回任何东西(当然,undefined不用说都懂)
  • 而koa 中的next 函数本质就是代码中的dispatch函数,每次触发调用next都会去调用diapatch去处理,最终的结果会被包裹一层promise return出去
  • Promise 中的类方法实现过程,请参考,这边不多说
  • 总之,我们调用next执行下一个中间件的时候可以使用 async 和 await来处理异步代码,这样就可以从靠前执行的中间件中拿到异步数据进行下一步处理

最后,欢迎留言、讨论,笔者会持续关注viewers的动态信息,以便及时更新文档