摘要:所以我們分析這個(gè)新需求的效果我們?cè)诤瘮?shù)執(zhí)行到一半時(shí),執(zhí)行了,的返回值為后續(xù)函數(shù)的執(zhí)行返回值。也就是說,我們?cè)谥刑幚?,直接調(diào)用隊(duì)列中的下一個(gè)函數(shù)即可然后監(jiān)聽和回調(diào),即可在當(dāng)前函數(shù)中獲取到返回值拿到返回值后就可以執(zhí)行我們后續(xù)的代碼。
最近想到了一個(gè)自認(rèn)為很有意思的面試題
如何實(shí)現(xiàn)一個(gè)compose函數(shù)。
函數(shù)接收數(shù)個(gè)參數(shù),參數(shù)均為Function類型,右側(cè)函數(shù)的執(zhí)行結(jié)果將作為左側(cè)函數(shù)執(zhí)行的參數(shù)來調(diào)用。
compose(arg => `${arg}%`, arg => arg.toFixed(2), arg => arg + 10)(5) // 15.00% compose(arg => arg.toFixed(2), arg => arg + 10)(5) // 15.00 compose(arg => arg + 10)(5) // 15
執(zhí)行結(jié)果如上述代碼,有興趣的同學(xué)可以先自己實(shí)現(xiàn)一下再來看后續(xù)的。
1.0實(shí)現(xiàn)方案大致的思路為:
獲取所有的參數(shù)
調(diào)用最后一個(gè)函數(shù),并接收返回值
如果沒有后續(xù)的函數(shù),返回?cái)?shù)據(jù),如果有,將返回值放入下一個(gè)函數(shù)中執(zhí)行
所以這種情況用遞歸來實(shí)現(xiàn)會(huì)比較清晰一些
function compose (...funcs) { return function exec (arg) { let func = funcs.pop() let result = func(arg) // 執(zhí)行函數(shù),獲取返回值 // 如果后續(xù)還有函數(shù),將返回值放入下一個(gè)函數(shù)執(zhí)行 // 如果后續(xù)沒有了,直接返回 return funcs.length ? exec(result) : result } }
這樣,我們就實(shí)現(xiàn)了上述的compose函數(shù)。
真是可喜可賀,可喜可賀。
本文完。
好了,如果現(xiàn)實(shí)生活中開發(fā)做需求也是如此爽快不做作就好了,但是,產(chǎn)品總是會(huì)來的,需求總是會(huì)改的。
2.0需求變更我們現(xiàn)在有如下要求,函數(shù)需要支持Promise對(duì)象,而且要兼容普通函數(shù)的方式。
示例代碼如下:
// 為方便閱讀修改的排版 compose( arg => new Promise((resolve, reject) => setTimeout(_ => resolve(arg.toFixed(2)), 1000 ) ), arg => arg + 10 )(5).then(data => { console.log(data) // 15.00 })
我們有如下代碼調(diào)用,對(duì)toFixed函數(shù)的調(diào)用添加1000ms的延遲。讓用戶覺得這個(gè)函數(shù)執(zhí)行很慢,方便下次優(yōu)化
所以,我們就需要去修改compose函數(shù)了。
我們之前的代碼只能支持普通函數(shù)的處理,現(xiàn)在因?yàn)樘砑恿?b>Promise對(duì)象的原因,所以我們要進(jìn)行如下修改:
首先,異步函數(shù)改為同步函數(shù)是不存在的readFile/readFileSync這類除外。
所以,最簡單的方式就是,我們將普通函數(shù)改為異步函數(shù),也就是在普通函數(shù)外包一層Promise。
function compose (...funcs) { return function exec (arg) { return new Promise((resolve, reject) => { let func = funcs.pop() let result = promiseify(func(arg)) // 執(zhí)行函數(shù),獲取返回值,并將返回值轉(zhuǎn)換為`Promise`對(duì)象 // 注冊(cè)`Promise`的`then`事件,并在里邊進(jìn)行下一次函數(shù)執(zhí)行的準(zhǔn)備 // 判斷后續(xù)是否還存在函數(shù),如果有,繼續(xù)執(zhí)行 // 如果沒有,直接返回結(jié)果 result.then(data => funcs.length ? exec(data).then(resolve).catch(reject) : resolve(data) ).catch(reject) }) } } // 判斷參數(shù)是否為`Promise` function isPromise (pro) { return pro instanceof Promise } // 將參數(shù)轉(zhuǎn)換為`Promise` function promiseify (pro) { // 如果結(jié)果為`Promise`,直接返回 if (isPromise(pro)) return pro // 如果結(jié)果為這些基本類型,說明是普通函數(shù) // 我們給他包一層`Promise.resolve` if (["string", "number", "regexp", "object"].includes(typeof pro)) return Promise.resolve(pro) }
我們針對(duì)compose代碼的改動(dòng)主要是集中在這幾處:
將compose的返回值改為了Promise對(duì)象,這個(gè)是必然的,因?yàn)閮?nèi)部可能會(huì)包含Promise參數(shù),所以我們一定要返回一個(gè)Promise對(duì)象
將各個(gè)函數(shù)執(zhí)行的返回值包裝為了Promise對(duì)象,為了統(tǒng)一返回值。
處理函數(shù)返回值,監(jiān)聽then和catch、并將resolve和reject傳遞了過去。
3.0終極版現(xiàn)在,我們又得到了一個(gè)新的需求,我們想要在其中某些函數(shù)執(zhí)行中跳過部分代碼,先執(zhí)行后續(xù)的函數(shù),等到后續(xù)函數(shù)執(zhí)行完后,再拿到返回值執(zhí)行剩余的代碼:
compose( data => new Promise((resolve, reject) => resolve(data + 2.5)), data => new Promise((resolve, reject) => resolve(data + 2.5)), async function c (data, next) { // async/await為Promise語法糖,不贅述 data += 10 // 數(shù)值 + 10 let result = await next(data) // 先執(zhí)行后續(xù)的代碼 result -= 5 // 數(shù)值 - 5 return result }, (data, next) => new Promise((resolve, reject) => { next(data).then(data => { data = data / 100 // 將數(shù)值除以100限制百分比 resolve(`${data}%`) }).catch(reject) // 先執(zhí)行后續(xù)的代碼 }), function d (data) { return data + 20 } )(15).then(console.log) // 0.45%
拿到需求后,陷入沉思。。。
好好地順序執(zhí)行代碼,突然就變成了這個(gè)鳥樣,隨時(shí)可能會(huì)跳到后邊的函數(shù)去。
所以我們分析這個(gè)新需求的效果:
我們?cè)诤瘮?shù)執(zhí)行到一半時(shí),執(zhí)行了next,next的返回值為后續(xù)函數(shù)的執(zhí)行返回值。
也就是說,我們?cè)?b>next中處理,直接調(diào)用隊(duì)列中的下一個(gè)函數(shù)即可;
然后監(jiān)聽then和catch回調(diào),即可在當(dāng)前函數(shù)中獲取到返回值;
拿到返回值后就可以執(zhí)行我們后續(xù)的代碼。
然后他的實(shí)現(xiàn)呢,也是非常的簡單,我們只需要修改如下代碼即可完成操作:
// 在這里會(huì)強(qiáng)行調(diào)用`exec`并傳入?yún)?shù) // 而`exec`的執(zhí)行,則意味著`funcs`集合中又一個(gè)函數(shù)被從隊(duì)列中取出來 promiseify(func(arg, arg => exec(arg)))
也就是說,我們會(huì)提前執(zhí)行下一個(gè)函數(shù),而且下一個(gè)函數(shù)的then事件注冊(cè)是在我們當(dāng)前函數(shù)內(nèi)部的,當(dāng)我們拿到返回值后,就可以進(jìn)行后續(xù)的處理了。
而我們所有的函數(shù)是存放在一個(gè)隊(duì)列里的,在我們提前執(zhí)行完畢該函數(shù)后,后續(xù)的執(zhí)行也就不會(huì)再出現(xiàn)了。避免了一個(gè)函數(shù)被重復(fù)執(zhí)行的問題。
如果看到這里已經(jīng)很明白了,那么恭喜,你已經(jīng)了解了實(shí)現(xiàn)koajs最核心的代碼:
中間件的實(shí)現(xiàn)方式、洋蔥模型
想必現(xiàn)在整個(gè)函數(shù)周遭散發(fā)著洋蔥的味道。
參考資料koa-compose
相關(guān)示例代碼倉庫1.0,普通函數(shù)
2.0,Promise函數(shù)
3.0,支持洋蔥模型
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/93790.html
摘要:壞的味道指的是應(yīng)該被修改,被重構(gòu)的代碼,不具有可讀性,復(fù)用性,判斷邏輯復(fù)雜,冗余代碼。它們通常能指出代碼用途和實(shí)現(xiàn)手法之間的語義距離。把所有和這個(gè)變量相關(guān)的代碼新建一個(gè)類放入。但這往往不夠,請(qǐng)反復(fù)運(yùn)用將某些行為移入類,直到者的協(xié)議一致為止。 壞的味道:指的是應(yīng)該被修改,被重構(gòu)的代碼,不具有可讀性,復(fù)用性,判斷邏輯復(fù)雜,冗余代碼。應(yīng)該使用各種重構(gòu)的手法去改變它! Duplicated...
摘要:壞味道的代碼重復(fù)代碼會(huì)自動(dòng)標(biāo)注重復(fù)的代碼。一般都是遇到真實(shí)情況后才考慮得到霰彈式修改添加或修改一個(gè)功能引發(fā)多個(gè)類相應(yīng)修改遇到這種情況可以移動(dòng)代碼,將需要修改的代碼都放在同一個(gè)類下。被拒絕的遺贈(zèng)子類應(yīng)該繼承超類的函數(shù)和數(shù)據(jù)。 壞味道的代碼 重復(fù)代碼 idea會(huì)自動(dòng)標(biāo)注重復(fù)的代碼。一般重復(fù)代碼就是可以重構(gòu)的點(diǎn)。 同一個(gè)類的兩個(gè)函數(shù)還有相同的表達(dá)式,這時(shí)需要提煉出重復(fù)代碼。 兩個(gè)互為兄弟的...
摘要:無論如何,單元測試一直是一中非常重要卻常常被忽視的技能。在實(shí)踐中,重構(gòu)的要求是很高的它需要有足夠詳盡的單元測試,需要有持續(xù)集成的環(huán)境,需要隨時(shí)隨地在小步伐地永遠(yuǎn)讓代碼處于可工作狀態(tài)下去進(jìn)行改善。 showImg(https://segmentfault.com/img/bVbttWF?w=1000&h=528); 五月初的時(shí)候朋友和我說《重構(gòu)》出第 2 版了,我才興沖沖地下單,花了一個(gè)...
摘要:,看了這樣的解釋,或許讓你更摸不著頭腦了。彈出彈出在這個(gè)例子中,只要會(huì)使用瀏覽器的朋友,都能看得出來完全繼承了的屬性和方法,否則是無法解釋的,因?yàn)樵谥胁]有定義屬性和,那么按常理推斷在的實(shí)例對(duì)象中,并不會(huì)出現(xiàn)這兩個(gè)屬性。 快速刪除尾部數(shù)組 var arr=[1,2,3,4,5]; arr.length=3; console.log(arr)//[1,2,3] 直接改變數(shù)組的length...
摘要:前端基本功示例代碼一點(diǎn)這里前端基本功示例代碼二點(diǎn)這里一像素偽類實(shí)現(xiàn)對(duì)于老項(xiàng)目,有沒有什么辦法能兼容的尷尬問題了,個(gè)人認(rèn)為偽類是比較完美的方法了。 前端基本功-示例代碼 (一) 點(diǎn)這里前端基本功-示例代碼 (二) 點(diǎn)這里 1.一像素 偽類 + transform 實(shí)現(xiàn)對(duì)于老項(xiàng)目,有沒有什么辦法能兼容1px的尷尬問題了,個(gè)人認(rèn)為偽類+transform是比較完美的方法了。 原理是把原先元素...
閱讀 3078·2021-11-25 09:43
閱讀 2844·2021-10-09 09:44
閱讀 2970·2021-09-22 15:49
閱讀 2763·2021-09-01 11:43
閱讀 2696·2019-08-30 14:16
閱讀 604·2019-08-29 17:24
閱讀 3172·2019-08-29 14:00
閱讀 1536·2019-08-29 13:05