这篇文章探讨js的洋葱模型以它的实现原理,洋葱模型顾名思义,指的是办法的执行像洋葱一样,一层一层往里执行,直到中心点后,再一层一层往外进去。
下面的图片取自koa中间件的流程管制图,react-redux
的中间件也采纳了一样的原理。
let fn1 = (next) => { console.log(1) next() console.log(2)}let fn2 = (next) => { console.log(3) next() console.log(4)}let fn3 = (next) => { console.log(5) next() console.log(6)}const middlewares = [fn1, fn2, fn3];compose(middlewares)();/* result:135642*/
合并后的middlewares
数组,通过compose
解决打印进去的后果是: 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){ let fn = middlewarw[index] || args; if(typeof fn !== "function") return; let next = ()=> dispatch(index+1); fn(next); } }};
当然咱们也心愿碰上异步函数,也能失常的执行:
function asyncFn() { return new Promise((resolve, reject) => { setTimeout(() => { console.log("delay..."); resolve(); }, 1000); });}let fn1 = async (next) => { console.log(1) await next() console.log(2)}let fn2 = async (next) => { console.log(3) await asyncFn(); await next() console.log(4)}let fn3 = async (next) => { console.log(5) await next() console.log(6)};function compose(middlewarw) { return function (args) { dispatch(0); function dispatch(index) { let fn = middlewarw[index] || args; if (typeof fn !== "function") return Promise.resolve(); let next = () => dispatch(index + 1); // 给执行函数增加返回胜利的Promise.resolve return Promise.resolve(fn(next)) } }};compose([fn1,fn2,fn3])();
react-redux 的中间件实现
react-redux
的中间件是这样实现compose
函数的:
function compose(middlewarw) { return middlewarw.reduce((total, next) => (...args) => total(next(...args)));}
react-redux
中间件执行的函数很不好了解,不过咱们能够拆开它外面的函数来一步步剖析:
let fn1 = (next) => { return ()=>{ console.log(1) next() console.log(2) }}let fn2 = (next) => { return ()=>{ console.log(3) next() console.log(4) }}let fn3 = (next) => { return ()=>{ console.log(5) next() console.log(6) }}let dispatch = compose([fn1,fn2,fn3])(()=> console.log("dispatch"));dispatch();
middlewarw
通过reduce
叠加,每次都将上一次的后果返回给下一个函数作参数:
// 第1次 reduce 的返回值,变成 total 传递到下一次arg => fn1(() => fn2(arg));// 第2次 reduce 的返回值,持续作为下一次的 totalarg => (arg => fn1(() => fn2(arg)))(() => fn3(arg));
或者将compose
转成比拟好了解的函数迭代模式:
function compose(middlewarw) { return function(cb) { function dispatch(index){ let fn = middlewarw[index]; let next = ()=>dispatch(index+1); // 下一次的函数执行 // 如果不存在下一个函数了,拿到传参外面的函数执行,这里须要保障传参是一个函数,对应的是redux外面的dispatch参数 fn ? fn(next)() : cb() } // 最终返回一个函数 return ()=> dispatch(0); }};