探究一道價(jià)值25k的螞蟻金服異步串行面試題
前言
朋友去面試螞蟻金服,遇到了一道面試題,乍一看感覺(jué)挺簡(jiǎn)單的,但是實(shí)現(xiàn)起來(lái)發(fā)現(xiàn)內(nèi)部值得一提的點(diǎn)還是挺多的。
先看題目:
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const subFlow = createFlow([() => delay(1000).then(() => log("c"))]); createFlow([ () => log("a"), () => log("b"), subFlow, [() => delay(1000).then(() => log("d")), () => log("e")], ]).run(() => { console.log("done"); }); // 需要按照 a,b,延遲1秒,c,延遲1秒,d,e, done 的順序打印
按照上面的測(cè)試用例,實(shí)現(xiàn) createFlow:
- flow 是指一系列 effects 組成的邏輯片段。
- flow 支持嵌套。
- effects 的執(zhí)行只需要支持串行。
分析
先以入?yún)⒎治?,createFlow 接受一個(gè)數(shù)組作為參數(shù)(按照題意里面的每一項(xiàng)應(yīng)該叫做 effect),排除掉一些重復(fù)的項(xiàng),我們把參數(shù)數(shù)組中的每一項(xiàng)整理歸類(lèi)一下,總共有如下幾種類(lèi)型:
普通函數(shù):
() => log("a");
延遲函數(shù)(Promise):
() => delay(1000).then(() => log("d"));
另一個(gè) flow:
const subFlow = createFlow([() => delay(1000).then(() => log("c"))]);
用數(shù)組包裹的上述三項(xiàng)。
實(shí)現(xiàn)
先把參數(shù)淺拷貝一份(編寫(xiě)庫(kù)函數(shù),盡量不要影響用戶傳入的參數(shù)是個(gè)原則),再簡(jiǎn)單的扁平化 flat 一下。(處理情況 4)
function createFlow(effects = []) { let sources = effects.slice().flat(); }
觀察題意,createFlow 并不會(huì)讓方法開(kāi)始執(zhí)行,需要 .run() 之后才會(huì)開(kāi)始執(zhí)行,所以先定義好這個(gè)函數(shù):
function createFlow(effects = []) { let sources = effects.slice().flat(); function run(callback) { while (sources.length) { const task = sources.shift(); } callback?.(); } }
這里我選擇用 while 循環(huán)依次處理數(shù)組中的每個(gè) effect,便于隨時(shí)中斷。
對(duì)于函數(shù)類(lèi)型的 effect,直接執(zhí)行它:
function createFlow(effects = []) { let sources = effects.slice().flat(); function run(callback) { while (sources.length) { const task = sources.shift(); if (typeof task === "function") { const res = task(); } } // 在所有任務(wù)執(zhí)行完畢后 執(zhí)行傳入的回調(diào)函數(shù) callback?.(); } return { run, isFlow: true, }; }
這里拿到了函數(shù)的返回值 res,有一個(gè)情況別忘了,就是 effect 返回的是一個(gè) Promise,比如這種情況:
() => delay(1000).then(() => log("d"));
那么拿到返回值后,這里直接簡(jiǎn)化判斷,看返回值是否有 then 屬性來(lái)判斷它是否是一個(gè) Promise(生產(chǎn)環(huán)境請(qǐng)選擇更加嚴(yán)謹(jǐn)?shù)姆椒ǎ?br />
if (res?.then) { res.then(createFlow(sources).run); return; }
這里我選擇中斷本次的 flow 執(zhí)行,并且用剩下的 sources 去建立一個(gè)新的 flow,并且在上一個(gè) Promise 的 then 方法里再去異步的開(kāi)啟新的 flow 的 run。
這樣,上面延遲 1s 后的 Promise 被 resolve 之后,剩下的 sources 任務(wù)數(shù)組會(huì)被新的 flow.run() 驅(qū)動(dòng),繼續(xù)執(zhí)行。
接下來(lái)再處理 effect 是另一個(gè) flow 的情況,注意上面編寫(xiě)的大致函數(shù)體,我們已經(jīng)讓 createFlow 這個(gè)函數(shù)返回值帶上 isFlow
這個(gè)標(biāo)記,用來(lái)判斷它是否是一個(gè) flow。
// 把callback放到下一個(gè)flow的callback時(shí)機(jī)里執(zhí)行 const next = () => createFlow(sources).run(callback) if (typeof task === "function") { const res = task(); if (res?.then) { res.then(next); return; } } else if (task?.isFlow) { task.run(next); return; }
看 else if 的部分,直接調(diào)用傳入的 flow 的 run,把剩下的 sources 創(chuàng)建的新的 flow,并且把這一輪的 callback 放入到新的 flow 的 callback 位置。在所有的任務(wù)都結(jié)束后再執(zhí)行。
定義一個(gè) next 方法,用來(lái)在遇到異步任務(wù)或者另一個(gè) flow 的時(shí)候
這樣,參數(shù)中傳入的 flow 執(zhí)行完畢后,才會(huì)繼續(xù)執(zhí)行剩下的任務(wù),并且在最后執(zhí)行 callback。
完整代碼
function createFlow(effects = []) { let sources = effects.slice().flat(); function run(callback) { while (sources.length) { const task = sources.shift(); // 把callback放到下一個(gè)flow的callback時(shí)機(jī)里執(zhí)行 const next = () => createFlow(sources).run(callback) if (typeof task === "function") { const res = task(); if (res?.then) { res.then(next); return; } } else if (task?.isFlow) { task.run(next); return; } } callback?.(); } return { run, isFlow: true, }; } const delay = () => new Promise((resolve) => setTimeout(resolve, 1000)); createFlow([ () => console.log("a"), () => console.log("b"), createFlow([() => console.log("c")]), [() => delay().then(() => console.log("d")), () => console.log("e")], ]).run();
總結(jié)
這道面試題主要的目的是考察對(duì)于異步串行流的控制,巧妙的利用自身的遞歸設(shè)計(jì)來(lái)處理傳入的參數(shù)也是一個(gè) flow的情況,在編寫(xiě)題目的過(guò)程中展示你對(duì) Promise 的熟練運(yùn)用,一定會(huì)讓面試官對(duì)你刮目相看的~
到此這篇關(guān)于探究一道價(jià)值25k的螞蟻金服異步串行面試題的文章就介紹到這了,更多相關(guān)異步串行面試題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
回車(chē)直接實(shí)現(xiàn)點(diǎn)擊某按鈕的效果即觸發(fā)單擊事件
這篇文章主要介紹了回車(chē)直接實(shí)現(xiàn)點(diǎn)擊某按鈕的效果即觸發(fā)單擊事件,需要的朋友可以參考下2014-02-02淺談js停止事件冒泡 阻止瀏覽器的默認(rèn)行為(阻止超連接 #)
下面小編就為大家?guī)?lái)一篇淺談js停止事件冒泡 阻止瀏覽器的默認(rèn)行為(阻止超連接 #)。2017-02-02微信小程序webview組件交互,內(nèi)聯(lián)h5頁(yè)面并網(wǎng)頁(yè)實(shí)現(xiàn)微信支付實(shí)現(xiàn)解析
這篇文章主要介紹了小程序webview組件交互,內(nèi)聯(lián)h5頁(yè)面并網(wǎng)頁(yè)實(shí)現(xiàn)微信支付實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08layui 數(shù)據(jù)表格 點(diǎn)擊分頁(yè)按鈕 監(jiān)聽(tīng)事件的實(shí)例
今天小編就為大家分享一篇layui 數(shù)據(jù)表格 點(diǎn)擊分頁(yè)按鈕 監(jiān)聽(tīng)事件的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09