一起养成写作习惯!这是我参与「日新计划 4 月更文应战」的第3天,点击检查活动概况。

前因

最近周末闲着无事,在上刷着沸点,刷着文章,忽然看到这么一个面试题

完结一个办法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)

面试题:写一个办法,完结最大并发数的并发恳求

经过图片能够看到,咱们传入函数的延时都是不一样的,但是打印出来的结果是依照传入的次序打印的。

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

我们能够试试。

我们假如有其它办法欢迎谈论,一起交流。