在实现 compose 前,我们先来了解一下什么洋葱模型。
洋葱模型
洋葱模型,它就是 Koa
中间件的一种串行机制,并且是支持异步的。Koa中间件机制就是函数式组合概念 Compose的概念,将一组需要顺序执行的函数复合为一个函数,外层函数的参数实际是内层函数的返回值。洋葱圈模型可以形象表示这种机制。
下面是官方的一张图,即著名的洋葱圈模型:
我们再来看一下中间件的执行顺序:
下面是一个表达 “洋葱模型” 的经典案例:
const Koa = require("koa");
const app = new Koa();
app.use(asycn (ctx, next) => {
console.log(1);
await next();
console.log(2);
});
app.use(asycn (ctx, next) => {
console.log(3);
await next();
console.log(4);
});
app.use(asycn (ctx, next) => {
console.log(5);
await next();
console.log(6);
});
app.listen(3000);
根据中间件的执行顺序,上面代码输出的结果为:1 => 3 => 5 => 6 => 4 => 2
compose 实现
洋葱圈的核心实现是 compose 函数,我们来盘点一下 compose 的各种实现。
Koa 中 compose 的实现方式
koa 递归实现
module.exports.compose = (middlewares = []) => {
// 保证 middlewares 为数组
if (!Array.isArray(middlewares)) {
middlewares = Array.from(arguments);
}
// 保证 middlewares 的每一项为函数
if (middlewares.some(fn => typeof fn !== 'function')) {
throw new TypeError('Middleware must be composed of functions!');
}
return function () {
// 取出第一个中间件函数执行
return dispatch(0);
// 递归函数
function dispatch(i) {
// 取出第 i 个中间件并执行
let fn = middlewares[i];
// 中间件函数不存在或者所有的中间件函数都执行完,返回成功态的 Promise
if (!fn) {
return Promise.resolve();
}
// 执行后返回成功态的 Promise
return Promise.resolve(
fn(function next() { // 执行下一个中间件函数
return dispatch(i + 1);
})
);
}
};
};
Koa Reduce 实现
module.exports.compose = (middlewares = []) => () => {
// 保证 middlewares 为数组
if (!Array.isArray(middlewares)) {
middlewares = Array.from(arguments);
}
// 保证 middlewares 内的每一项为函数
if (middlewares.some(fn => typeof fn !== 'function')) {
throw new TypeError('Middleware must be composed of functions!');
}
if (middlewares.length === 0) {
// 没有中间件函数时返回 成功态的Promise
return Promise.resolve();
} else if (middlewares.length === 1) {
// 只有一个中间件函数时,执行当前的中间件函数
return Promise.resolve(middlewares[0].call(null, () => Promise.resolve()));
} else {
// compose reduce实现
return middlewares
.map(item => item)
.reverse()
.reduce((pre, cur) => () => cur(() => pre(() => {})))();
}
};
Koa Class 实现
class ComposeClass {
constructor() {
this.middlewares = [];
this.index = 0;
}
middleware(middlewares) {
this.middlewares = middlewares;
return this.dispatch(this.index);
}
dispatch(index) {
const fn = this.middlewares[index];
if (!fn) {
return Promise.resolve();
}
this.index += 1;
return Promise.resolve(fn(this.next.bind(this)));
}
// 指定下一个中间件的执行
next() {
return this.dispatch(this.index);
}
}
module.exports.compose = (middlewares = []) => {
if (!Array.isArray(middlewares)) {
middlewares = Array.from(arguments);
}
if (middlewares.some(fn => typeof fn !== 'function')) {
throw new TypeError('Middleware must be composed of functions!');
}
return () => {
return new ComposeClass().middleware(middlewares);
};
};
express 中的 compose 实现
express 递归实现
module.exports.compose = (middlewares = []) => {
//
if (!Array.isArray(middlewares)) {
middlewares = Array.from(arguments);
}
if (middlewares.some(fn => typeof fn !== 'function')) {
throw new TypeError('Middleware must be composed of functions!');
}
return async () => {
let idx = 0;
async function next() {
if (idx === middlewares.length) {
return Promise.resolve();
}
if (idx < middlewares.length) {
return Promise.resolve(middlewares[idx++](next));
}
}
return await next();
};
};
Redux 中的 compose 实现
Redux Reduce 实现
module.exports.compose = (middlewares = []) => {
if (!Array.isArray(middlewares)) {
middlewares = Array.from(arguments);
}
if (middlewares.length === 0) {
return arg => arg;
}
if (middlewares.some(fn => typeof fn !== 'function')) {
throw new TypeError('Middleware must be composed of functions!');
}
return (next = async () => {}) => middlewares.reduce((a, b) => arg => a(() => b(arg)))(next);
};
Redux ReduceRight 实现
/**
* @description: 利用reduceRight实现洋葱圈
* @param {middlewares:中间件数组}
* @return {高阶函数}
*/
module.exports.compose = (middlewares = []) => {
if (!Array.isArray(middlewares)) {
middlewares = Array.from(arguments);
}
if (middlewares.some(fn => typeof fn !== 'function')) {
throw new TypeError('Middleware must be composed of functions!');
}
// reduceRight:从右向左做累加,第二个参数为初始值
// 比如:middlewares = [f1, f2, f3],执行过程如下:
// 第一次 --> a = () => {}, b = f3,结果:() => f3(() => {})
// 第二次 --> a = () => f3(() => {}), b = f2,结果:() => f2(() => f3(() => {}))
// 第三次 --> a = () => f2(() => f3(() => {})),b = f1,结果:() => f1(() => f2(() => f3(() => {})))
return () =>
middlewares.reduceRight(
(a, b) => () => b(a),
() => {}
)();
};
Redux ReduxRight Promise 实现
module.exports.compose = (middlewares = []) => {
if (!Array.isArray(middlewares)) {
middlewares = Array.from(arguments);
}
if (middlewares.some(fn => typeof fn !== 'function')) {
throw new TypeError('Middleware must be composed of functions!');
}
return () =>
Promise.resolve(
middlewares.reduceRight(
(a, b) => () => Promise.resolve(b(a)),
() => Promise.resolve()
)()
);
};
责任链模式实现
class Handle {
constructor(middleware) {
this.middleware = middleware;
this.next = null;
}
setNext(nextHandle) {
this.next = nextHandle;
}
}
module.exports.compose = (middlewares = []) => {
if (!Array.isArray(middlewares)) {
middlewares = Array.from(arguments);
}
if (middlewares.some(fn => typeof fn !== 'function')) {
throw new TypeError('Middleware must be composed of functions!');
}
const handles = [];
for (let i = 0; i < middlewares.length; i++) {
const handle = new Handle(middlewares[i]);
handles.push(handle);
}
for (let i = 0; i < handles.length; i++) {
if (handles[i + 1]) {
handles[i].setNext(handles[i + 1]);
}
}
return function func() {
return innerFunc(handles[0]);
function innerFunc(currentHandle = handles[0]) {
if (!currentHandle) {
return Promise.resolve();
}
return Promise.resolve(currentHandle.middleware(() => innerFunc(currentHandle.next)));
}
};
};
List 列表递归实现
const isArray = action => Array.isArray(action)?true:false;
const isFunction = middlewares => middlewares.every(middleware => typeof middleware === 'function');
module.exports.compose = (args) => async () => {
let middlewares = [...args]
if(!isArray(middlewares)) throw new TypeError('middlewares must be Array');
if(!isFunction(middlewares)){
throw new TypeError('middleware must be Function')
}
if(middlewares.length === 0){
return null;
}
//生成链表
//{next:{fn:b,next:{fn:next}}}
function getStatck(statck){
if(middlewares.length>0){
let fn = middlewares.shift()
statck.next = { fn }
getStatck(statck.next)
return statck;
}
}
let statck = getStatck({});
let filber = statck.next;
return dispatch(filber)
function dispatch(filber){
if(!filber){
return () =>{}
}
return filber.fn(next = async()=>{
await dispatch(filber.next)
})
}
}