前端控制并发数
在现代Web应用开发中,提高用户体验的同时要确保服务器负载平衡是至关重要的。控制前端发起请求的并发数,即限制同一时间内进行处理的请求数量,是一种有效的策略。本文将详细介绍前端控制并发数的几种常见做法,并且提供具体的代码示例。
队列机制
队列是实现并发控制常用的数据结构。它遵循先进先出(FIFO)的原则,能够按照请求到达的顺序逐一处理。
示例:基于 Promise 的请求队列
在JavaScript中,可以利用Promise
来实现一个简单的请求队列。下面是一个示例代码:
javascript
class PromiseQueue {
constructor(maxConcurrent) {
this.maxConcurrent = maxConcurrent;
this.queue = [];
this.currentCount = 0;
}
add(promiseCreator) {
return new Promise((resolve, reject) => {
this.queue.push({
promiseCreator,
resolve,
reject
});
this.tryToRun();
});
}
tryToRun() {
if (this.queue.length === 0 || this.currentCount >= this.maxConcurrent) {
return;
}
this.currentCount++;
const { promiseCreator, resolve, reject } = this.queue.shift();
promiseCreator().then(resolve, reject).finally(() => {
this.currentCount--;
this.tryToRun();
});
}
}
利用此队列可以如下方式控制并发:
javascript
// 实例化队列,设置最大并发数为 2
const queue = new PromiseQueue(2);
// 添加请求到队列
queue.add(() => fetch(url1)).then(handleResponse);
queue.add(() => fetch(url2)).then(handleResponse);
queue.add(() => fetch(url3)).then(handleResponse);
在此示例中,只有两个请求会立即执行,第三个请求会等待前两个中的某个完成后再开始。
控制并发的库
市面上有许多专门用于控制并发的JavaScript库,如p-limit
, async
等。
示例:使用 p-limit 控制并发
p-limit
是一个轻量级的库,可以方便地限制并发数:
javascript
import pLimit from 'p-limit';
const limit = pLimit(2);
const input = [
limit(() => fetch(url1)),
limit(() => fetch(url2)),
limit(() => fetch(url3)),
limit(() => fetch(url4)),
];
// 只有 2 个请求被同时处理
Promise.all(input).then(results => {
// 处理结果
});
在上述代码中,limit
函数会保证同时只有两个请求在处理。
XMLHttpRequest的abort方法
在使用XMLHttpRequest(XHR)对象发送请求时,可以在请求数量超过设定的并发限制时调用abort
方法来取消请求。
示例:使用XMLHttpRequest和abort控制并发
javascript
const MAX_CONCURRENT = 2;
let concurrentCount = 0;
const xhrPool = [];
function fetchWithXhr(url) {
if (concurrentCount >= MAX_CONCURRENT) {
setTimeout(() => fetchWithXhr(url), 200); // 200ms 后重试
return;
}
concurrentCount++;
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => {
handleResponse(xhr.response);
concurrentCount--;
if (xhrPool.length > 0) {
const newXhr = xhrPool.shift();
fetchWithXhr(newXhr.url);
}
};
xhr.onerror = xhr.onabort = () => {
concurrentCount--;
};
xhr.send();
}
function abortExtraRequests() {
while (concurrentCount > MAX_CONCURRENT) {
const xhrToAbort = xhrPool.pop();
xhrToAbort.abort();
}
}
// 发起请求
fetchWithXhr("https://api.example.com/data1");
fetchWithXhr("https://api.example.com/data2");
fetchWithXhr("https://api.example.com/data3");
在此示例中,超出并发限制的请求将会被推迟执行,而非立即取消;并且可以使用abortExtraRequests函数对并发数目进行校验,去除超过的部分。
Fetch API的AbortController
Fetch API提供了AbortController
接口,通过它可以随时取消一个或多个请求。
示例:使用AbortController控制并发
javascript
const MAX_CONCURRENT = 2;
let concurrentCount = 0;
const controllerQueue = new Map();
async function fetchWithAbortController(url) {
if (concurrentCount >= MAX_CONCURRENT) {
await new Promise(resolve => setTimeout(resolve, 200)); // 200ms 后重试
return fetchWithAbortController(url);
}
const controller = new AbortController();
concurrentCount++;
controllerQueue.set(url, controller);
try {
const response = await fetch(url, { signal: controller.signal });
handleResponse(await response.json());
} catch (error) {
// 错误处理
} finally {
controllerQueue.delete(url);
concurrentCount--;
if (__yourCondition) {
controllerQueue.get(__specificUrl).abort(); // 取消下一个请求
}
}
}
// 发起请求
fetchWithAbortController("https://api.example.com/data1");
fetchWithAbortController("https://api.example.com/data2");
fetchWithAbortController("https://api.example.com/data3");
在这个例子中,AbortController
用于取消等待队列中的请求,而且每个请求都有自己的AbortController
实例。
通过以上方法,可以有效控制前端并发数,避免服务器过载且优化用户体验。不需修改服务器配置或缩放服务器资源即可实现前端请求的优雅管理。这些策略对于构建响应速度快、资源使用高效的Web应用至关重要。