不知道为什么,最近大数据给我推荐了几篇前端做控制并发的文章,技术实现是没任何问题,使用到的技术核心也不错,就是应用的地方就有点问题了。
浏览器 HTTP 请求 pending
打开浏览器,network 可以看每个请求的状态,正常来说,pending 表示请求已经发起,等待后端响应。
而现代浏览器,当一个浏览器对同一域名的并发连接数达到限制时,额外的请求会被浏览器暂时搁置,这些请求还未发送到服务器。正常来说浏览器都是 6 个。当请求少于 6 个了才会继续发起下一个请求。所以有一个优化就是用不同域名进行请求,一般也就分开静态资源和接口不同域名。
所以那些应用在控制请求并发的文章,个人觉得没太大意义。如果核心是分享前端如何创建一个池子和释放,那就另说。
代码验证
node 启动一个测试接口
// server.js
const http = require('http');
const url = require('url');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const pathname = parsedUrl.pathname;
const method = req.method;
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
if (method === 'GET' && pathname === '/') {
res.statusCode = 200;
console.log('Have connect: ' + parsedUrl.query.index);
setTimeout(() => {
res.end(JSON.stringify({ message: 'Hello World!' }));
}, 6000)
} else if (method === 'POST' && pathname === '/data') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
const data = JSON.parse(body);
res.statusCode = 200;
res.end(JSON.stringify({ message: `You sent: ${JSON.stringify(data)}` }));
});
} else {
res.statusCode = 404;
res.end(JSON.stringify({ message: 'Not Found' }));
}
});
server.listen(port, hostname, () => {
console.log(`Server is running at http://${hostname}:${port}/`);
});
js 调用(数量可以随便定)
function getHello(index) {
axios.get("http://127.0.0.1:3000?index=" + index).then((res) => {
console.log(res);
});
}
for (let i = 0; i < 100; i++) {
getHello(i);
}
看接口的输出,很容易就能看见,每隔 6 秒钟,接口才能接受到请求,而浏览器看 network 是 100 个请求同时发出,都是 pending 状态。
控制并发,池子创建和释放
那些文章想要的其实也很简单,就是把一系列的任务放到一个数组,运行到某个任务,就从池子里面释放,当同时存在 N 个任务的时候就等候,一个任务完成了继续从池子里面取任务。
const concurrency = 6;
let taskList = [];
let activeCount = 0;
const task = (index) => {
const time = getRandomTime(1, 9);
console.log(`task:${index}--${taskList.length}`);
setTimeout(() => {
activeCount--;
taskOver();
}, time);
};
const getRandomTime = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
const randomInt = Math.floor(Math.random() * (max - min + 1)) + min;
return randomInt * 1000;
};
const taskOver = () => {
if (taskList.length > 0 && activeCount < concurrency) {
activeCount++;
const taskItem = taskList.shift();
taskItem.task(taskItem.index);
}
};
for (let i = 0; i < 100; i++) {
taskList.push({
index: i,
task: task,
});
}
for (let i = 0; i < concurrency; i++) {
taskOver();
}
如果时间设置成固定值,控制台输出就很有规律,6 个一组。如果真的要用到控制并发,实际逻辑应该会更复杂,如果拿来控制请求的并发,还是算了。