内容简介:首先写一个简单的中间件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第二篇: 图解中间件源码执行过程》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Domain-Driven Design Distilled
Vaughn Vernon / Addison-Wesley Professional / 2016-6-2 / USD 36.99
Domain-Driven Design (DDD) software modeling delivers powerful results in practice, not just in theory, which is why developers worldwide are rapidly moving to adopt it. Now, for the first time, there......一起来看看 《Domain-Driven Design Distilled》 这本书的介绍吧!