内容简介:首先写一个简单的中间件demo:很明显中间件执行顺序是这样的:你可以理解为koa2会先按照中间件注册顺序执行next()之前的代码, 执行完到底部之后, 返回往前执行next()之后的代码。
首先写一个简单的中间件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是先按照你中间件的顺序去注册执行
- 然后会进入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已经存了两个中间件。
- 这个时候你请求一个路由比如
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是一直存在的。
这个时候可以看到fn就是ctx1。
注意
// next即就是通过dispatch(i+1)来执行下一个中间件 dispatch.bind(null, i + 1) 复制代码
这个时候开始进入第一个中间件执行第一句console.log('开始执行中间件1')
这里也能看到next指的就是前面提到的dispatch.bind。
然后我们继续单步调试进入这句
// ctx1中的 await next() 复制代码
此时又重新进入compose(), 继续执行下一个中间件, i=1
取出第二个中间件函数ctx2。
此时进入第二个中间件ctx2开始执行console.log('开始执行中间件2')
继续单步调试
此时i=2,fx=undefined
// 这个洋葱模型的最后做一个兜底的处理 if (!fn) return Promise.resolve() 复制代码
执行中间件ctx2的第二句console
以上所述就是小编给大家介绍的《koa2第二篇: 图解中间件源码执行过程》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。