前端如何控制并發(fā)請求舉例詳解
前言
什么情況需要前端控制并發(fā)請求,在需要多次才能請求完所需數(shù)據(jù)的時候。比如接口一次返回,數(shù)據(jù)很多,讓瀏覽器渲染卡頓甚至崩潰,這時候我們可以分批同時發(fā)出6個請求,這樣就可以避免卡頓或者崩潰。
那么前端如何控制并發(fā)請求呢?
前端控制并發(fā)請求的關(guān)鍵思路
比如有 20 個請求,需要按照 3
個一組,第一組請求完畢后再請求第二組,以此類推。
關(guān)鍵思路,把請求方法和請求參數(shù)使用一個數(shù)組存起來,然后每次請求3個,請求完畢后再請求下一個3個。每組請求返回后,把結(jié)果保存起來,等所有請求都返回后,再把所有結(jié)果返回。
api 設(shè)計
pControl
: 并發(fā)請求控制器, 傳遞最大并發(fā)數(shù);add
: 添加請求和參數(shù);start
: 開始請求,返回promise, 請求完畢后通過.then
獲取所有結(jié)果;
代碼實現(xiàn)
function pControl(limit) { const taskQueue = [] // {task: Function, params: any[]}[] return { add, start } function add(task, params) { taskQueue.push({ task, params }) } function start() { return runAllTasks() } function runAllTasks() { const allResults = [] return new Promise((resolve) => { runTask() function runTask() { if (taskQueue.length === 0) { // 遞歸結(jié)束 return resolve(allResults) } const needRunSize = Math.min(taskQueue.length, limit) const tasks = taskQueue.splice(0, needRunSize) const promises = tasks.map(({ task, params }) => task(params)) Promise.all(promises).then((resList) => { allResults.push(...resList) // NOTE 遞歸調(diào)用的位置很關(guān)鍵 runTask() }) } }) } }
關(guān)鍵代碼解讀
pControl
: 這個函數(shù)返回一個對象,包含add
和start
兩個方法,add
用來添加任務(wù)和參數(shù),start
用來開始請求,是一個閉包。runAllTasks
: 返回一個promise
,然后在new Promise
內(nèi)部遞歸地執(zhí)行runTask
, runTask 通過Promise.all
執(zhí)行并發(fā)請求,在Promise.all().then()
再次調(diào)用runTask
,實現(xiàn)一組請求返回,再執(zhí)行第二組請求。
實現(xiàn)分組等待的關(guān)鍵是在
.then
中遞歸調(diào)用。
思考: runAllTasks 可以使用循環(huán)實現(xiàn)嗎?
能,需要使用 async 和 for 循環(huán) + await
:
async function runAllTasks2() { const allResults = [] const groupArr = [] let startIndex = 0 // 劃分分組 while (startIndex < taskQueue.length) { const arr = taskQueue.slice(startIndex, startIndex + limit) groupArr.push(arr) startIndex += limit } for (let index = 0; index < groupArr.length; index++) { const pList = groupArr[index].map(({ task, params }) => task(params)) const res = await Promise.all(pList) allResults.push(...res) } return allResults }
在 for 中循環(huán)中不能使用
.then
,否則下一次循環(huán)不會等待上一次循環(huán)。
使用 for of
迭代實現(xiàn):
async function runAllTasks2() { const allResults = [] const groupArr = [] let startIndex = 0 // 劃分分組 while (startIndex < taskQueue.length) { const arr = taskQueue.slice(startIndex, startIndex + limit) groupArr.push(arr) startIndex += limit } // 迭代分組 const it = groupArr.entries() for (const [key, value] of it) { const pList = value.map(({ task, params }) => task(params)) const res = await Promise.all(pList) allResults.push(...res) } return allResults }
循環(huán)和 Promise 結(jié)合是怎樣使用的呢?
for
、 while
、 for...of
等命令式循環(huán)結(jié)構(gòu),想要在循環(huán)中實現(xiàn)等待效果,必須使用 async
函數(shù)包裹循環(huán)中的 await
,不能使用 .then
。
forEach
、 map
、 filter
等函數(shù)式循環(huán)結(jié)構(gòu),不支持等待效果,因為這些函數(shù)式循環(huán)結(jié)構(gòu)是同步的,不支持等待。
async
和循環(huán)
+await
結(jié)合,實現(xiàn)循環(huán)之間等待效果。
promise.then
和遞歸
結(jié)合,實現(xiàn)循環(huán)之間等待效果。
完善 api,讓其更加易用
- 設(shè)置默認(rèn)參數(shù):給
pControl
設(shè)置一個合適的默認(rèn)值,設(shè)置為6
,因為同一個域名在,瀏覽器的并發(fā)請求是 6 個。 - start給回調(diào):通過回調(diào)能拿到每個分組的請求結(jié)果和知道當(dāng)前完成的請求數(shù)量。
這兩個改進(jìn)很簡單。先看用法:
const asyncTaskControl = pControl() // 默認(rèn) 6 asyncTaskControl.add(task, params1) asyncTaskControl.add(task, params2) // ... asyncTaskControl.add(task, params10) asyncTaskControl.start((res, doneSize) => { // 獲取每組請求的結(jié)果 和當(dāng)前完成了多少請求 console.log(res) // [{index:number,result:data}] console.log(doneSize) }).then(allResults => { // 所有請求結(jié)果 console.log(allResults) })
start 回調(diào)有什么作用呢?
方便使用者拿當(dāng)前并發(fā)請求的結(jié)果,方便計算完成進(jìn)度。
把上述功能封裝成 p-control npm 包發(fā)布
可通過 npm i p-control
下載使用。
小結(jié)
.then
和遞歸結(jié)合,實現(xiàn)異步任務(wù)之間等待;for
、while
等循環(huán)和async
+await
結(jié)合使用,實現(xiàn)異步任務(wù)之間等待;- 使用
Promise.all
實現(xiàn)多個異步任務(wù)并發(fā)執(zhí)行。
到此這篇關(guān)于前端如何控制并發(fā)請求的文章就介紹到這了,更多相關(guān)前端控制并發(fā)請求內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
傳參安全處理window.btoa base64加密,線性對稱加密
這篇文章主要介紹了傳參安全處理window.btoa base64加密,線性對稱加密,需要的朋友可以參考下2023-07-07JavaScript實現(xiàn)鼠標(biāo)點(diǎn)擊導(dǎo)航欄變色特效
本文給大家分享一段基于js代碼實現(xiàn)的鼠標(biāo)點(diǎn)擊導(dǎo)航欄變色效果,代碼簡單易懂,非常不錯,具有參考借鑒價值,需要的的朋友參考下2017-02-02詳解JavaScript如何控制并發(fā)請求數(shù)量
某些情況下,我們可能需要對需要執(zhí)行的多個異步任務(wù)進(jìn)行異步數(shù)量控制,只允許固定數(shù)量的任務(wù)執(zhí)行,本文為大家整理了JS控制并發(fā)請求數(shù)量的相關(guān)代碼,希望對大家有所幫助2024-01-01獲取當(dāng)前按鈕或者h(yuǎn)tml的ID名稱實例(推薦)
下面小編就為大家?guī)硪黄@取當(dāng)前按鈕或者h(yuǎn)tml的ID名稱實例(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06js或者jquery判斷圖片是否加載完成實現(xiàn)代碼
需要獲得圖片的寬度和高度,有些js或者jquery代碼在還沒有加載完圖片時就執(zhí)行了,這個問題該怎么解決呢?接下來分別介紹下js與jquery提供的方法2013-03-03