洋葱模型指的是方法的执行像洋葱一样,一层一层往里执行,直到中心点后,再一层一层往外出来。
上面的图片取自koa中间件的流程控制图,react-redux
的中间件也采用了一样的原理。
const fn1 = (next) => {
console.log(1)
next()
console.log(2)
}
const fn2 = (next) => {
console.log(3)
next()
console.log(4)
}
const fn3 = (next) => {
console.log(5)
next()
console.log(6)
}
const middlewares = [fn1, fn2, fn3];
compose(middlewares)();
执行结果:1 3 5 6 4 2
compose
函数执行视图:
+----------------------------------------------------------------------------------+
| |
| fn1 |
| |
| +-----------------------------------------------------------+ |
| | | |
| | fn2 | |
| | | |
| | +---------------------------------+ | |
| | | | | |
| action | action | fn3 | action | action |
| 1 | 3 | | 4 | 2 |
| | | action action | | |
| | | 5 6 | | |
| | | | | |
+----------------------------------------------------------------------------------------------->
| | | | | |
| | | | | |
| | +---------------------------------+ | |
| +-----------------------------------------------------------+ |
+----------------------------------------------------------------------------------+
分析
compose
必须返回的是一个函数,并且每次函数执行,都需要将下一个函数作为参数传给它,这样才能够让方法一层层的执行下去,直到最里面一层:
function compose(middlewarw) {
return function(args){
dispatch(0);
function dispatch(index){
const fn = middlewarw[index] || args;
if(typeof fn !== "function") return;
const next = ()=> dispatch(index+1);
fn(next);
}
}
};
异步函数也能的处理:
function asyncFn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("delay...");
resolve();
}, 1000);
});
}
const fn1 = async (next) => {
console.log(1)
await next()
console.log(2)
}
const fn2 = async (next) => {
console.log(3)
await asyncFn();
await next()
console.log(4)
}
const fn3 = async (next) => {
console.log(5)
await next()
console.log(6)
};
function compose(middlewarw) {
return function (args) {
dispatch(0);
function dispatch(index) {
const fn = middlewarw[index] || args;
if (typeof fn !== "function") return Promise.resolve();
const next = () => dispatch(index + 1);
// 给执行函数添加返回成功的Promise.resolve
return Promise.resolve(fn(next))
}
}
};
compose([fn1,fn2,fn3])();
Redux中间件
redux
的中间件compose
函数如下:
function compose(middlewarw) {
return middlewarw.reduce((total, next) => (...args) => total(next(...args)));
}
redux
的中间件函数很不好理解,这里可以将它拆开进行分析:
const fn1 = (next) => {
return ()=>{
console.log(1)
next()
console.log(2)
}
}
const fn2 = (next) => {
return ()=>{
console.log(3)
next()
console.log(4)
}
}
const fn3 = (next) => {
return ()=>{
console.log(5)
next()
console.log(6)
}
}
const dispatch = compose([fn1,fn2,fn3])(()=> console.log("dispatch"));
dispatch();
middlewarw
经过reduce
叠加,每次都将上一次的结果返回给下一个函数作参数:
// 第1次 reduce 的返回值,变成 total 传递到下一次
arg => fn1(() => fn2(arg));
// 第2次 reduce 的返回值,继续作为下一次的 total
arg => (arg => fn1(() => fn2(arg)))(() => fn3(arg));
或者将compose
转成比较好理解的函数迭代形式:
function compose(middlewarw) {
return function(cb) {
function dispatch(index){
const fn = middlewarw[index];
const next = ()=>dispatch(index+1); // 下一次的函数执行
// 如果不存在下一个函数了,拿到传参里面的函数执行,这里需要保证传参是一个函数,对应的是redux里面的dispatch参数
fn ? fn(next)() : cb()
}
// 最终返回一个函数
return ()=> dispatch(0);
}
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。