30行程式碼實現一個帶併發數限制的fetch請求函式
早上吃早餐邊逛掘金的時候看到一個面試題,看下面的各位大佬各顯神通,我也手癢,拿起我的機械鍵盤一頓亂敲,發現只需要30行程式碼就可以實現,似不似很膩害:ghost::ghost:,快來跟我往下翻:stuck_out_tongue:
原題

題目來源: 記一道控制併發數的前端面試題【手動維護 HTTP 請求排隊】
思路
讀題
快速過一遍題目,可以獲得這幾個主要資訊:
callback
解題
- 批量請求
要實現批量請求,而且並不需要按順序發起請求(如果需要按順序可以存入佇列中,按優先順序則可以存入優先佇列中),所以這裡我們存入陣列中即可,然後進行遍歷,取出數字中的每一項丟去 fetch
中進行呼叫。
- 可控制併發度
控制併發數,一個簡單的辦法就是對陣列進行切片,分成一段一段,完成一段後再呼叫另一段。這裡我們可以使用遞迴或者迴圈來實現,我覺得遞迴比較直觀,所以這裡使用遞迴來實現。
- 全部請求結束,執行
callback
因為是非同步請求,我們無法寄希望於安裝正常的順序在函式呼叫後執行,但是每次 fetch
有返回結果會呼叫 then
或者 catch
,我們可以在這個時候判斷請求陣列是否為空就可以知道是否全部被呼叫完
寫題
這一步就沒什麼可以說的了,擼起袖子就是敲~
function handleFetchQueue(urls, max, callback) { const requestArr = []; urls.forEach((item, idx) => { const i = Math.floor(idx / max); if (requestArr[i]) { requestArr[i].push(item) } else { requestArr[i] = [item] } }); const handleSubRequests = (subReqs) => { const results = []; subReqs.forEach(req => { fetch(req).then(res => { if (results.push(res) === max) { if (requestArr.length < 1) { 'function' === typeof callback && callback(results) } else { handleSubRequests(requestArr.shift(), requestArr, max) } } }).catch(e => { results.push(e) }) }) }; handleSubRequests(requestArr.shift()) } 複製程式碼
這裡需要稍微提一下的兩個小技巧:
Math.floor(idx / max) results.push(res)
附上完整測試程式碼:
function handleFetchQueue(urls, max, callback) { const requestArr = []; urls.forEach((item, idx) => { const i = Math.floor(idx / max); if (requestArr[i]) { requestArr[i].push(item) } else { requestArr[i] = [item] } }); const handleSubRequests = (subReqs) => { const results = []; subReqs.forEach(req => { fetch(req).then(res => { if (results.push(res) === max) { if (requestArr.length < 1) { 'function' === typeof callback && callback(results) } else { handleSubRequests(requestArr.shift(), requestArr, max) } } }).catch(e => { results.push(e) }) }) }; handleSubRequests(requestArr.shift()) } const urls = Array.from({length: 10}, (v, k) => k); const fetch = function (idx) { return new Promise(resolve => { console.log(`start request ${idx}`); // 模擬請求時間 const timeout = parseInt(Math.random() * 1e4); setTimeout(() => { console.log(`end request ${idx}`); resolve(idx) }, timeout) }) }; const max = 4; const callback = () => { console.log('run callback'); }; handleFetchQueue(urls, max, callback); 複製程式碼
因為我在 Node 中執行,(lan)懶(ai)得(fa)丟(zuo)瀏覽器中去跑了,所以隨手模擬了一個 fetch
函式。
總結
通過 讀題 、 解題 和 寫題 三個步驟(自創的:stuck_out_tongue:),可以讓我們的思路非常清晰,非常 easy 的解決面試題。
題目敲了20分鐘程式碼解決,但是文章寫了一小時,太不容易了,覺得不錯請給我鼓個掌~~