likes
comments
collection
share

面试题:写一个方法,实现最大并发数的并发请求

作者站长头像
站长
· 阅读数 21

前因

实现一个方法multiRequestLimitNum(reqArr, limitNum),这个方法有以下功能

  1. 可以并发发请求
  2. 但是并发的请求数有限制,不能超过limitNum。
  3. 并发的请求每成功一个,就可以从reqArr中取一个去补上,满足最大并发数limitNum。
  4. 最后返回值需要按照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)

面试题:写一个方法,实现最大并发数的并发请求

通过图片可以看到,我们传入函数的延时都是不一样的,但是打印出来的结果是按照传入的顺序打印的。

并且并发的请求是成功一个,才会取下个请求去请求。

大家可以试试。

大家如果有其它方法欢迎评论,一起沟通。