代码实现
function fetchWithConcurrency(urls, maxConcurrent) {
let currentIndex = 0;
const results = [];
const promises = [];
function fetchNext() {
while (promises.length < maxConcurrent && currentIndex < urls.length) {
const url = urls[currentIndex++];
const promise = fetch(url)
.then(response => {
results.push(response);
return response;
})
.catch(error => {
results.push(error);
throw error;
})
.finally(() => {
// 完成后补充新请求
fetchNext();
});
promises.push(promise);
}
return Promise.allSettled(promises);
}
return fetchNext();
}
// 使用示例
const urls = [
'https://example.com/api1',
'https://example.com/api2',
'https://example.com/api3',
'https://example.com/api4',
'https://example.com/api5',
'https://example.com/api6',
'https://example.com/api7'
];
fetchWithConcurrency(urls, 5)
.then(results => {
console.log('All requests completed:', results);
});
并发控制对性能的影响
- 资源利用:通过限制并发请求数为5,可以避免因过多请求耗尽系统资源(如网络带宽、服务器连接数等)。在高并发场景下,如果没有并发控制,可能导致网络拥塞,所有请求的响应时间都会变长。
- 响应时间:对于单个请求而言,并发控制可能会使请求排队等待执行,理论上可能会增加单个请求的等待时间。但从整体来看,由于避免了资源过度竞争,反而可能提升了整体的响应性能,确保每个请求都能在合理时间内获得响应。
- 稳定性:合理的并发控制有助于提高系统的稳定性,防止因过多请求导致服务崩溃或响应异常。
Fetch API 和 Promise 在这种场景下的底层原理
- Fetch API:
- 原理:Fetch API 是基于Promise的网络请求接口,用于在浏览器中发起HTTP请求。它返回一个Promise对象,该对象在请求完成(无论成功或失败)时被解决。当调用
fetch(url)
时,浏览器会在网络线程中发起实际的网络请求,同时JavaScript主线程不会被阻塞。
- Promise:
- 原理:Promise 是一种处理异步操作的方式,它代表一个尚未完成但预期将来会完成的操作。Promise 有三种状态:
pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。在上述代码中,fetch
返回的Promise对象被链式调用,then
方法用于处理成功状态,catch
方法用于处理失败状态,finally
方法无论成功或失败都会执行。
- 任务队列与事件循环:
- 任务队列:JavaScript 是单线程的,所有异步操作(如Promise、
setTimeout
等)都会被放入任务队列。当主线程空闲时,会从任务队列中取出任务执行。在fetch
发起请求后,网络请求在后台执行,当请求完成(成功或失败)时,相应的回调函数(then
或catch
中的函数)会被放入任务队列。
- 事件循环:事件循环机制不断检查主线程是否空闲,如果空闲则从任务队列中取出一个任务放入主线程执行。在上述代码中,当
fetch
请求完成后,then
、catch
和finally
中的回调函数会被放入任务队列,等待事件循环将它们放入主线程执行。这样,即使有多个fetch
请求并发执行,JavaScript 也能通过任务队列和事件循环有序地处理它们的结果。