前言 📝
在前端进行并发请求的时候,前端浏览器为了减轻服务器的压力,限制同一域名下并发请求的最大数量是6~8个(每个浏览器的最大并发数量不一致)。既然浏览器帮助我们控制了最大并发量,那为什么还要手动控制并发呢?原因很简单,因为面试官会问。其实还有一点就是,浏览器默认的等待机制是全局的,无法做到更细粒度的控制。我们手写控制并发的情况下,自己可以实现不同的排队策略、错误处理策略、超时处理策略、控制最大并发数量等。
如下所示,定义一个 result 用来缓存请求队列返回的结果,定义一个参数 index 记录下一次请求的索引。然后我们按照并发量去循环发送网络请求。在循环请求的时候,finally中判断是否继续发送请求还是终止请求返回所有结果。
注意:
result存储的是返回结果,但是并发多个请求返回的顺序并不一致,所以在fetchApi函数内定了i,来记录当前网络请求的索引。用于将网络请求的返回结果存储在对应的位置。
<script>
const requestQueue = (urls = [], maxNum = 6) => {
return new Promise((resolve, reject) => {
if(!urls.length) {
resolve([])
return
}
const result = []
let index = 0
async function fetchApi() {
const i = index
const url = urls[i]
index++
try {
const resp = await fetch(url)
const res = await resp.json()
result[i] = res
} catch(err) {
result[i] = err
} finally {
if(result.length === urls.length) {
resolve(result)
return
}
if(index < urls.length) {
fetchApi()
}
}
}
const count = Math.min(maxNum, urls.length)
for(let i = 0; i < count; i++) {
fetchApi()
}
})
}
// 测试代码
const urls = new Array(8).fill('http://127.0.0.1:3000')
requestQueue(urls, 4).then(res => console.log(res))
</script>
我们简单用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}/`);
});
我这里并发数量改成6,接口换成100个试一下:
const urls = new Array(100).fill('http://127.0.0.1:3000')
requestQueue(urls, 6).then(res => console.log(res))
我们可以看到和我们的预期一样,一次发送6个请求。正好发送了一百个网络请求,最后将返回值按照发送请求的顺序对应的放到数组里面全部返回。