js 中的洋蔥模型
來源:SegmentFault 思否
作者:chenwl
這篇文章探討js的洋蔥模型以它的實(shí)現(xiàn)原理,洋蔥模型顧名思義,指的是方法的執(zhí)行像洋蔥一樣,一層一層往里執(zhí)行,直到中心點(diǎn)后,再一層一層往外出來。

上面的圖片取自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數(shù)組,經(jīng)過compose處理打印出來的結(jié)果是:1、3、5、6、4、2,我們可以想到compose函數(shù)回的函數(shù)里面是怎么執(zhí)行的:
+----------------------------------------------------------------------------------+| || fn1 || || +-----------------------------------------------------------+ || | | || | fn2 | || | | || | +---------------------------------+ | || | | | | || action | action | fn3 | action | action || 1 | 3 | | 4 | 2 || | | action action | | || | | 5 6 | | || | | | | |+----------------------------------------------------------------------------------------------->| | | | | || | | | | || | +---------------------------------+ | || +-----------------------------------------------------------+ |+----------------------------------------------------------------------------------+
那么我們就開始編寫compose函數(shù),首先它必須返回的是一個函數(shù),并且每次函數(shù)執(zhí)行,都需要將下一個函數(shù)作為參數(shù)傳給它,這樣才能夠讓方法一層層的執(zhí)行下去,直到最里面一層:
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);}}};
當(dāng)然我們也希望碰上異步函數(shù),也能正常的執(zhí)行:
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);// 給執(zhí)行函數(shù)添加返回成功的Promise.resolvereturn Promise.resolve(fn(next))}}};compose([fn1,fn2,fn3])();
react-redux 的中間件實(shí)現(xiàn)
react-redux的中間件是這樣實(shí)現(xiàn)compose函數(shù)的:
function compose(middlewarw) {return middlewarw.reduce((total, next) => (...args) => total(next(...args)));}
react-redux中間件執(zhí)行的函數(shù)很不好理解,不過我們可以拆開它里面的函數(shù)來一步步分析:
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經(jīng)過reduce疊加,每次都將上一次的結(jié)果返回給下一個函數(shù)作參數(shù):
// 第1次 reduce 的返回值,變成 total 傳遞到下一次arg => fn1(() => fn2(arg));// 第2次 reduce 的返回值,繼續(xù)作為下一次的 totalarg => (arg => fn1(() => fn2(arg)))(() => fn3(arg));
或者將compose轉(zhuǎn)成比較好理解的函數(shù)迭代形式:
function compose(middlewarw) {return function(cb) {function dispatch(index){let fn = middlewarw[index];let next = ()=>dispatch(index+1); // 下一次的函數(shù)執(zhí)行// 如果不存在下一個函數(shù)了,拿到傳參里面的函數(shù)執(zhí)行,這里需要保證傳參是一個函數(shù),對應(yīng)的是redux里面的dispatch參數(shù)fn ? fn(next)() : cb()}// 最終返回一個函數(shù)return ()=> dispatch(0);}};

評論
圖片
表情
