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

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

内容简介:首先写一个简单的中间件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第二篇: 图解中间件源码执行过程》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

马化腾自述-我的互联网思维

马化腾自述-我的互联网思维

赵黎 / 石油工业出版社 / 2014-8-1 / 35

马化腾自述:我的互联网思维》讲述了些人说移动互联网就是加了“移动”两个字,互联网十几年了,移动互联网应该是个延伸。我的感受是,移动互联网远远不只是一个延伸,甚至是一个颠覆。互联网是一个开放交融、瞬息万变的大生态,企业作为互联网生态里面的物种,需要像自然界的生物一样,各个方面都具有与生态系统汇接、和谐、共生的特性。开放和分享并不是一个宣传口号,也不是一个简单的概念。开放很多时候被看作一种姿态,但是我......一起来看看 《马化腾自述-我的互联网思维》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

html转js在线工具
html转js在线工具

html转js在线工具