koa2第二篇: 图解中间件源码执行过程

栏目: Node.js · 发布时间: 7年前

内容简介:首先写一个简单的中间件demo:很明显中间件执行顺序是这样的:你可以理解为koa2会先按照中间件注册顺序执行next()之前的代码, 执行完到底部之后, 返回往前执行next()之后的代码。
koa2第二篇: 图解中间件源码执行过程

首先写一个简单的中间件demo:

const Koa = require('koa')
const app = new Koa()
const port = 3000

const ctx1 = async (ctx, next) => {
    console.log('开始执行中间件1')
    await next()
    ctx.response.type = 'text/html'
    ctx.response.body = '<h3>hello world</h3>'
    console.log('结束执行中间件1')
}

app.use(ctx1)
app.use(async function ctx2 (ctx, next) {
    console.log('开始执行中间件2')
    await next()
    console.log('结束执行中间件2')
})

app.listen(port, () => {
    console.log(`server is running on the port: ${port}`)
})

复制代码

很明显中间件执行顺序是这样的:

开始执行中间件1
开始执行中间件2
结束执行中间件2
结束执行中间件1
复制代码

你可以理解为koa2会先按照中间件注册顺序执行next()之前的代码, 执行完到底部之后, 返回往前执行next()之后的代码。

重点是我们需要koa2源码究竟是怎么样执行的? 现在开始调试模式进入koa2源码一探究竟。

  • 首先在两个中间件注册的地方打了断点
koa2第二篇: 图解中间件源码执行过程
  • 我们可以看到koa2是先按照你中间件的顺序去注册执行
koa2第二篇: 图解中间件源码执行过程
koa2第二篇: 图解中间件源码执行过程
  • 然后会进入callback. 这是因为
// 应用程序
app.listen(port, () => {
    console.log(`server is running on the port: ${port}`)
})

// 源码
 listen(...args) {
    debug('listen');
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }
复制代码

这个时候this.middleware已经存了两个中间件。

koa2第二篇: 图解中间件源码执行过程
  • 这个时候你请求一个路由比如
http://localhost:3000/a
复制代码

koa2的中间件处理就是在这个函数里面

callback() {
    // compose()这是处理中间件的执行顺序所在
  }
复制代码

于是我们进入这个koa-compose的源码看下:

'use strict'

/**
 * Expose compositor.
 */

module.exports = compose

/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */

function compose (middleware) {
  // 首先是一些中间件格式校验
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

  /**
   * @param {Object} context
   * @return {Promise}
   * @api public
   */

  return function (context, next) {
    // last called middleware #
    let index = -1
    // 返回一个函数, 从第一个中间件开始执行, 可以通过next()调用后续中间件
    return dispatch(0)
    // dispatch始终返回一个Promise对象
    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 {
        // next即就是通过dispatch(i+1)来执行下一个中间件
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        // 捕获中间件中发生的异常
        return Promise.reject(err)
      }
    }
  }
}

复制代码

此时i=0取出第一个中间件,由于闭包原因i是一直存在的。

koa2第二篇: 图解中间件源码执行过程

这个时候可以看到fn就是ctx1。

koa2第二篇: 图解中间件源码执行过程

注意

// next即就是通过dispatch(i+1)来执行下一个中间件
dispatch.bind(null, i + 1)
复制代码

这个时候开始进入第一个中间件执行第一句console.log('开始执行中间件1')

koa2第二篇: 图解中间件源码执行过程

这里也能看到next指的就是前面提到的dispatch.bind。

然后我们继续单步调试进入这句

// ctx1中的
await next()
复制代码

此时又重新进入compose(), 继续执行下一个中间件, i=1

koa2第二篇: 图解中间件源码执行过程

取出第二个中间件函数ctx2。

koa2第二篇: 图解中间件源码执行过程

此时进入第二个中间件ctx2开始执行console.log('开始执行中间件2')

koa2第二篇: 图解中间件源码执行过程

继续单步调试

koa2第二篇: 图解中间件源码执行过程

此时i=2,fx=undefined

koa2第二篇: 图解中间件源码执行过程
// 这个洋葱模型的最后做一个兜底的处理
if (!fn) return Promise.resolve()
复制代码

执行中间件ctx2的第二句console

koa2第二篇: 图解中间件源码执行过程

以上所述就是小编给大家介绍的《koa2第二篇: 图解中间件源码执行过程》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro JavaScript Design Patterns

Pro JavaScript Design Patterns

Dustin Diaz、Ross Harmes / Apress / 2007-12-16 / USD 44.99

As a web developer, you’ll already know that JavaScript™ is a powerful language, allowing you to add an impressive array of dynamic functionality to otherwise static web sites. But there is more power......一起来看看 《Pro JavaScript Design Patterns》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具