JavaScript控制并发请求

181 阅读2分钟

最近师兄面试时遇到了一道手撕题:有一url数组,其中有若干url请求,有一变量 maxNum ,表示最大并发的url请求数量,实现一个方法,完成对url的并发控制,将每个url的请求结果合并到一个数组中,并使其在数组中的位置与url在请求数组中的位置对应

大致思路是按照要求开始时从url数组中拿出 maxNum 个请求并发送,当其中有请求完成时,再从url数组中拿出新的请求填补,并将已完成请求的结果写入结果数组对应的位置,这样依次类推,直到urls里面的所有请求都执行完才终止

其实重点就两个:

一是保证请求的并发数量,当有请求完成时,开启新的请求进行补位

二是确保在请求结果异步返回的情况下,保证存储结果的顺序正确

直接上代码:

function concurRequests(urls,maxNum) {
  return new Promise(resolve=>{
    if(urls.length ===0){//如果urls为空,直接返回空数组
      resolve([])
      return
    }
    const results = []
    let index = 0//下一个待发送请求的下标
    let count = 0//已完成请求的数量
    async function sendRequest() {
      if(index === urls.length) return //当所有请求已发送时,不再发送请求
      const i = index //保存发送请求的下标,在接收结果时保证其存储位置与在urls数组中的位置对应
      const url = urls[index]
      index++
      try{
       const response = await fetch(url)
       results[i] = response
      }catch(err){
        results[i] = err
      }finally{
        count++//每完成一次请求让count++
        if(count === urls.length) {//所有请求完成时,返回results
          console.log('所有请求已完成')
          resolve(results)
        }
        sendRequest()//否则继续发送下一个请求
      }
      
    }
    const times = maxNum < urls.length ? maxNum : urls.length//考虑特殊情况,当最大并发数大于url数组总长度
    for (let i = 0; i < times; i++) {
      sendRequest() 
    }
  })
}

还需要注意的是对几种特殊情况的处理:

1.urls的长度为0时,results就没有值,此时应该返回空数组

2.maxNum大于urls的长度时,应该取的是urls的长度,否则则是取maxNum

3.对于请求结果错误的情况,也应该保存其错误结果

简单的测试一下:

const urls = [];
for (let i = 1; i <= 50; i++) {//总共50个请求
    urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
}
concurRequests(urls, 5).then(res => {//每次并发5个请求
    console.log(res);
})

可以看到每次并行发送5个请求 QQ录屏20230925114243.gif

结果保存也没有问题 1.jpg