前端如何控制并发请求

JackChouMine 2024-08-01 17:03:01 阅读 100

前端如何控制并发请求

前端控制并发请求的关键思路api 设计代码实现关键代码解读循环和 Promise 结合是怎样使用的呢?

完善 api,让其更加易用把上述功能封装成 `p-control` npm 包发布小结

什么情况需要前端控制并发请求,在需要多次才能请求完所需数据的时候。比如接口一次返回,数据很多,让浏览器渲染卡顿甚至崩溃,这时候我们可以分批同时发出6个请求,这样就可以避免卡顿或者崩溃。

那么前端如何控制并发请求呢?

前端控制并发请求的关键思路

比如有 20 个请求,需要按照 <code>3 个一组,第一组请求完毕后再请求第二组,以此类推。

关键思路,把请求方法和请求参数使用一个数组存起来,然后每次请求3个,请求完毕后再请求下一个3个。每组请求返回后,把结果保存起来,等所有请求都返回后,再把所有结果返回。

api 设计

pControl : 并发请求控制器, 传递最大并发数;

add : 添加请求和参数;

start : 开始请求,返回promise, 请求完毕后通过 .then 获取所有结果;

代码实现

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) {

// 递归结束

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 递归调用的位置很关键

runTask()

})

}

})

}

}

关键代码解读

pControl: 这个函数返回一个对象,包含 addstart 两个方法,add 用来添加任务和参数,start 用来开始请求,是一个闭包。

runAllTasks: 返回一个promise,然后在new Promise内部递归地执行runTask, runTask 通过Promise.all 执行并发请求,在Promise.all().then() 再次调用runTask,实现一组请求返回,再执行第二组请求。

实现分组等待的关键是在 .then 中递归调用。

思考: runAllTasks 可以使用循环实现吗?

能,需要使用 async 和 for 循环 + 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 中循环中不能使用 .then ,否则下一次循环不会等待上一次循环。

使用 for of 迭代实现:

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

}

循环和 Promise 结合是怎样使用的呢?

forwhilefor...of 等命令式循环结构,想要在循环中实现等待效果,必须使用 async 函数包裹循环中的 await ,不能使用 .then

forEachmapfilter 等函数式循环结构,不支持等待效果,因为这些函数式循环结构是同步的,不支持等待。

async循环 + await 结合,实现循环之间等待效果。

promise.then递归 结合,实现循环之间等待效果。

完善 api,让其更加易用

设置默认参数:给pControl设置一个合适的默认值,设置为6,因为同一个域名在,浏览器的并发请求是 6 个。start给回调:通过回调能拿到每个分组的请求结果和知道当前完成的请求数量。

这两个改进很简单。先看用法:

const asyncTaskControl = pControl() // 默认 6

asyncTaskControl.add(task, params1)

asyncTaskControl.add(task, params2)

// ...

asyncTaskControl.add(task, params10)

asyncTaskControl.start((res, doneSize) => {

// 获取每组请求的结果 和当前完成了多少请求

console.log(res) // [{index:number,result:data}]

console.log(doneSize)

}).then(allResults => {

// 所有请求结果

console.log(allResults)

})

start 回调有什么作用呢?

方便使用者拿当前并发请求的结果,方便计算完成进度。

把上述功能封装成 p-control npm 包发布

npm: p-control

可通过 npm i p-control 下载使用。

小结

.then 和递归结合,实现异步任务之间等待;forwhile等循环和async + await结合使用,实现异步任务之间等待;使用Promise.all实现多个异步任务并发执行。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。