面试题:写一个方法,实现最大并发数的并发请求
前因
实现一个方法multiRequestLimitNum(reqArr, limitNum)
,这个方法有以下功能
- 可以并发发请求
- 但是并发的请求数有限制,不能超过limitNum。
- 并发的请求每成功一个,就可以从reqArr中取一个去补上,满足最大并发数limitNum。
- 最后返回值需要按照reqArr的顺序返回。
分析
一开始看到并发发请求,就会想到Promise.all
,它可以实现请求的并发,但是它并不能控制最大并发数。
而且它要成功一个,就要补上一个。目前Promise.all
应该是做不到的,只能另辟蹊径。
现有的api都无法满足,那只能手写方法来实现。
第一步
我们先判断当前reqArr的长度是否大于最大并发数。如果大于则无需处理,如果小于则最大并发数是reqArr的长度。
然后遍历,我这里使用while循环,定义个索引i
等于0,然后i++
,每次都执行请求函数,直到i等于最大并发数。
这样模拟并发。
第二步
我们的i++是在请求函数里面执行的,把当前索引赋值cur
变量,执行reqArr对应i索引的函数,
把返回值赋值给一开始定义的reqArr长度的数组resArr。
但是执行函数是异步的,这时候i
是已经变化了,可以通过前面赋值的cur
变量来把返回值放到resArr对应的位置。
这样子可以保证返回的顺序是按照reqArr的顺序。
第三步
如果此时 i
还不等于reqArr的长度,则递归调用请求函数,这样就满足成功一个请求,就补上一个请求。
直到i等于reqArr的长度的时候,则代表reqArr的请求已经完成,把数组resArr resolve返回即可。
代码如下:
function multiRequestLimitNum (reqArr, limitNum) {
const reqLen = reqArr.length
const resArr = new Array(reqLen)
let i = 0
return new Promise(async (resolve, reject) => {
const maxNum = reqLen >= limitNum ? limitNum : reqLen
while (i < maxNum) {
reqFn()
}
async function reqFn () {
const cur = i++
const fn = reqArr[cur]
const data = await fn().catch((err) => { return err })
resArr[cur] = data
if (i === reqArr.length) resolve(resArr)
else reqFn()
}
})
}
我们用上面的代码来验证一下,用setTimeout来模拟发出的请求
function req (res, delay) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(res)
}, delay)
})
}
multiRequestLimitNum([
req.bind(null, 1, 1000),
req.bind(null, 2, 500),
req.bind(null, 3, 2000),
req.bind(null, 4, 100)],
2)
通过图片可以看到,我们传入函数的延时都是不一样的,但是打印出来的结果是按照传入的顺序打印的。
并且并发的请求是成功一个,才会取下个请求去请求。
大家可以试试。
大家如果有其它方法欢迎评论,一起沟通。
转载自:https://juejin.cn/post/7082393784767496206