熟悉 koa-compose 中间件源码
歌德曾说:读一本好书,就是在和高尚的人谈话。 同理可得:读源码,也算是和作者的一种学习交流的方式。
参考
koa-compose
中间件是指连贯整个 Koa 应用程序,并共享资源的独立插件,“连贯”对应“next”,“共享资源对应context”。
在koa
的文档上有个非常代表性的中间件 gif
图
compose
处理中间件流程主要是用compose函数
compose执行逻辑:
- 接收一个参数,校验参数是数组,且校验数组中的每一项是函数。
- 返回一个函数,这个函数接收两个参数,分别是
context
和next
,这个函数最后返回Promise
。
/**
* 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
return dispatch(0)
function dispatch(i){
// 省略,下文讲述
}
}
}
复制代码
dispatch
function dispatch (i) {
// 取出数组里的 fn1, fn2, fn3...
let fn = middleware[i]
//执行compose(stack)({})时,执行中间件数组里的第一个fn,执行过程中执行到next(),也就是执行了dispatch.bind(null, i + 1),会去执行中间件数组里的下一个fn,以此类推
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
}
复制代码
- 完整
# 三个细节
1.fn里多次调用next函数,这里用闭包index保存计数很牛逼
2.最后一个fn无next,返回Promise.resolve()
3.错误捕获由上一个fn来包裹执行
function dispatch (i) {
// 一个函数中多次调用报错
// await next() 这里的index是闭包数据,每一次掉用dispatch都会更新,若有一次fn执行了两次next,更新会有冲突
// await next()
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
// 取出数组里的 fn1, fn2, fn3...
let fn = middleware[i]
// 最后 相等,next 为 undefined
if (i === middleware.length) fn = next
// 直接返回 Promise.resolve()
if (!fn) return Promise.resolve()
//fn发生错误时,不执行上一个fn的next后面的代码,由上一个fn的catch函数执行相应逻辑
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
} catch (err) {
return Promise.reject(err)
}
}
复制代码
小记
1.bind函数中,如果函数不需要使用this
,一般会写成null
2.闭包可以用来计数,排查重复执行
function compose(){
let index = -1
dispatch(0)
dispatch(i){
// dispatch(0) 0《-1
// dispatch(1) 1《 0 1《1 error
if(i<=index) throw ..
index = i
return fn(dispatch(i+1))
}
}
fn(next){
next()
next()
}
复制代码
3.川哥yyds
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END