完成一个并发请求的函数 concurRequest
,它允许同时发送多个请求,但限制最大并发请求数
当全部请求全部收到响应结果后,返回一个数组,记录响应数据,同时保证响应顺序与参数的顺序一致
返回的 Promise 没有 reject 状态,只有 resolve,如果失败了只需提供失败原因即可
具体的实现思路如下:
concurRequest
函数接受两个参数urls
和maxNum
,分别是待请求的 URL 数组和最大并发请求数。如果传入的 URL 数组为空,函数会直接返回一个解决(resolved)状态的 Promise 对象。- 在
concurRequest
函数中,定义异步函数_request
,用于发起单个请求,并将结果存储在result
数组中。 - 初始化一些变量:
index
表示下一次要请求的 URL 下标,result
数组用于存储所有请求的结果,doneCount
表示已经完成的请求数量。 - 在
_request
函数中,我们先缓存下标i
和当前请求的 URLurl
,然后通过await
关键字发送请求并将结果存储在result
数组中对应的位置。 - 无论请求是成功还是失败,我们需要增加
doneCount
的值,并在finally
代码块中进行判断。如果所有请求都已完成,则使用resolve
方法解析 Promise,并将result
数组作为解决值。如果还有未完成的请求,继续调用_request
函数以发送下一个请求。 - 最后,在
concurRequest
函数中,我们使用一个for
循环来调用_request
函数,最多同时执行maxNum
个请求。
通过维护一个请求队列和计数器的方式,实现了并发请求的控制,保证最多同时发送指定数量的请求,并在所有请求完成后返回结果。这种实现方式可以在需要同时发起多个请求但又需要控制最大并发数的场景下使用。
const getUrls = (number = 10) => {
const result = []
for (let i = 1; i <= number; i++) {
result.push(`https://jsonplaceholder.typicode.com/todos/${i}`)
}
return result
}
/**
* 并发请求
* @param {string[]} urls 待请求的 url 数组
* @param {number} maxNum 最大并发数
*/
function concurRequest(urls, maxNum) {
if (urls.length === 0) {
return Promise.resolve([])
}
return new Promise((resolve) => {
let index = 0
const result = []
let doneCount = 0
async function _request() {
const i = index // 将 index 缓存到当前作用域下
const url = urls[index]
index++
try {
const res = await fetch(url)
result[i] = res
} catch (error) {
result[i] = error
} finally {
doneCount++
if (doneCount === urls.length) {
resolve(result)
}
if (index < urls.length)
_request()
}
}
for (let i = 0; i < Math.min(maxNum, urls.length); i++){
_request()
}
})
}
concurRequest(getUrls(10), 3).then(res => {
console.log(res)
})
与
Promise.all
以及Promise.allSettled
区别
- 最大并发数的限制:
concurRequest
函数一开始就限制了最大的并行请求数量maxNum
。而Promise.all
和Promise.allSettled
并不具有这样的限制,它们会立即启动所有传入的 Promise 。- 错误处理:
Promise.all
对 Promise 的错误处理是有敏感性的;如果 Promise 数组中任意一个 Promise 失败(rejected),整个Promise.all
就会立刻失败,并且失败的原因是第一个失败的 Promise 的结果。而Promise.allSettled
和concurRequest
不会因为单个 Promise 失败而导致整体失败,它们会等待所有传入的 Promise 都结束,无论其结果是解决(resolved)还是失败(rejected)。- 结果的返回:
concurRequest
函数返回的是一个和输入一致的数组,结果的顺序保证和输入的 Promise 的顺序一致,它们的索引一一对应。而Promise.all
和Promise.allSettled
则按照 Promise 完成的顺序确定结果的顺序。