koa-compose 源码解析
前言
接触过 nodejs 就很容易会了解到 koa,然后在 koa 中的中间件机制洋葱模型,那绝对是优美的实现。因为洋葱模型,让中间件可以在请求前后做更多的事
中间件是什么
简单点理解的话,其实就是一个函数,可以简单看下 koa 的使用例子
javascript
const koa = require('koa')
const app = new koa()
app.use(async (ctx, next) => {
console.log('第一个中间件开始')
next()
console.log('第一个中间件结束')
})
app.use(async (ctx, next) => {
console.log('第二个中间件开始')
next()
console.log('第二个中间件开始')
})
app.use((ctx, next) => {
console.log('第三个中间件开始')
next()
console.log('第三个中间件结束')
})
app.use((ctx) => {
console.log('响应')
ctx.body = '200'
})
app.listen(7001)
第一个中间件开始
第二个中间件开始
第三个中间件开始
响应
第三个中间件结束
第二个中间件结束
第一个中间件结束
看顺序基本知道 next 函数前面的依次执行完,然后在返回去依次执行 next 下面的部分
响应
这里疑惑了一下,由于惯性思维,没有去了解太多,以为 ctx.body 在这里就返回给客户端了。然而并不是,因为如果现在返回了,那中间件 next 下方的代码就没有什么意义了,因为请求已经结束,所以去看了 koa 的源码,响应是在中间件全部处理结束之后才返回
把中间件处理的结果交给返回体函数
这里和 express 的中间件就有区别了,express 的中间件是在最内部的函数返回的,然后请求就直接结束了
中间件源码
javascript
'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
// 主要的函数就是这个
return dispatch(0)
// 传入中间件函数的下标递归调用
function dispatch(i) {
//判断函数下标,不让next多次调用
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 {
执行当前函数并传入下一个函数并返回promise
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
} catch (err) {
return Promise.reject(err)
}
}
}
}