前端控制并发请求,使用Promise 在现代前端开发中,处理大量并发请求是一个常见的需求。如果不加以控制,大量并发请求可能导致服务器压力过大或客户端性能问题。因此,合理控制并发请求数量,确保系统的稳定性和响应能力至关重要。本文将详细探讨如何使用Promise来控制并发请求,以满足实际业务需求。
- 使用Promise.all()和Promise.allSettled() 首先,我们可以使用Promise.all()方法来实现并发请求。然而,当请求失败时,Promise.all()会立即失败且无法获取组内其他请求的返回值。为了解决这个问题,可以使用Promise.allSettled()方法,它能确保每个请求都能返回结果,无论成功还是失败。
const requests = [request1(), request2(), request3()];
Promise.allSettled(requests).then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(Request ${index + 1} succeeded with result:, result.value);
} else {
console.log(Request ${index + 1} failed with reason:, result.reason);
}
});
});
2. 维护运行池和等待队列
如果存在一个非常耗时的请求,可能会导致后续请求的阻塞,影响整体效率。为了解决这一问题,可以维护一个运行池和一个等待队列。运行池始终保持一定数量的请求并发,当一个请求完成时,从等待队列中取出一个新请求放入运行池中执行。
function sendRequest(requestList, limits, callback) { const pool = new Set(); let index = 0;
const runTask = async () => {
if (index >= requestList.length) return;
const request = requestList[index++];
const promise = request();
pool.add(promise);
promise.then(() => {
pool.delete(promise);
runTask();
}).catch(err => {
console.error('Request failed:', err);
pool.delete(promise);
runTask();
});
};
for (let i = 0; i < limits; i++) {
runTask();
}
const checkCompletion = setInterval(() => {
if (pool.size === 0) {
clearInterval(checkCompletion);
callback();
}
}, 100);
} 3. 使用p-limit库 p-limit库是一个优秀的并发限制库,通过简单的代码实现了并发处理功能。p-limit库使用Queue队列来管理并发请求,当队列大小小于最大并发数且队列不为空时,从队列中取出一个请求执行。
const pLimit = require('p-limit');
const limit = pLimit(5);
const input = [ () => fetchSomething('sindresorhus.com '), () => fetchSomething('ava.li '), () => fetchSomething('github.com ') ];
const promises = input.map(fn => limit(fn));
(async () => { const result = await Promise.all(promises); console.log(result); })(); 4. 手动实现并发请求控制 手动实现并发请求控制的方法是创建一个请求队列,并通过设置最大并发数来控制同时进行的请求数量。当一个请求完成时,从队列中取出下一个请求并执行。
function sendRequest(requestList, limits, callback) { const queue = [...requestList]; let activeCount = 0; const results = [];
const next = () => {
while (activeCount < limits && queue.length > 0) {
const request = queue.shift();
activeCount++;
request().then(result => {
results.push(result);
activeCount--;
next();
}).catch(err => {
console.error('Request failed:', err);
activeCount--;
next();
});
}
if (activeCount === 0 && queue.length === 0) {
callback(results);
}
};
next();
} 5. 处理请求结果和错误重试 在实际应用中,我们还需要处理请求结果和错误重试机制。可以通过增加一个参数来控制请求失败的重试次数。
function sendRequest(requestList, limits, retries, callback) { const queue = [...requestList]; let activeCount = 0; const results = [];
const next = () => {
while (activeCount < limits && queue.length > 0) {
const request = queue.shift();
activeCount++;
let attempt = 0;
const executeRequest = () => {
request().then(result => {
results.push(result);
activeCount--;
next();
}).catch(err => {
attempt++;
if (attempt <= retries) {
console.log(`Retrying request... Attempt ${attempt}`);
executeRequest();
} else {
console.error('Request failed after retries:', err);
activeCount--;
next();
}
});
};
executeRequest();
}
if (activeCount === 0 && queue.length === 0) {
callback(results);
}
};
next();
}