js异步队列的并发控制插件:
- js-asyncpool
- tiny-async-pool
- 背景:大家在开发中或多或少会用到上面两个插件
- 目的:理解一下这些插件做了什么事情,自己又该如何实现,为什么需要控制并发请求?
自己来实现一个并发请求函数concurrencyRequest(maxNum,urls),要求如下:
- 要求最大并发数 maxNum
- 每当有一个请求返回,就留下一个空位,可以增加新的请求
- 所有请求完成后,结果按照urls里面的顺序依次打出(发送请求的函数可以直接使用fetch即可)
各大浏览器并发请求限制数量6-8(同一域名有效)
- Stalled(阻塞):也即是从TCP连接建立完成,到真正可以传输数据之间的时间差(过长的请求是被阻塞了,处在队列中等待tcp连接)
- DNS Lookup:域名解析时间
- Initial connection(初始化连接):TCP建立连接的三次握手时间
- Request sent(发送请求)
- Waiting(等待响应)
- Content Download(下载):收到响应的第一个字节,到接受完最后一个字节的时间,就是下载时间。下载HTTP响应的时间(包含头部和响应体)
简单代码实现(其实插件的源码写的比较好一点):
js
// 并发请求函数
const concurrencyRequestTest = (poolLimit, urls, iteratorFn) => {
return new Promise(resolve => {
if (!urls || !urls.length) {
resolve([])
}
let i = 0, count = 1, resArr = [];
const request = async () => {
/* 队列已经请求完 */
if(i === urls.length) return
let url = urls[i]
i++;
// const res = await fetch(url)
const res = await iteratorFn(url)
resArr.push(res)
count++;
// 已经执行完的请求数量
if(count > urls.length) {
resolve(resArr)
}
request()
}
const maxNum = Math.min(poolLimit, urls.length)
for (let index = 0; index < maxNum; index++) {
request()
}
})
}
疑问:既然浏览器已经对http并发请求个数有限制,为什么我们前端还需要自己限制?
我简单写了20个请求,在谷歌下面同时发起。
情况一:并行的请求最大http个数是6个
情况二:这里我node使用了http2,然后同样的代码20个请求可以同时进行
使用http2
原因在于浏览器限制的不是http请求个数,而是TCP连接个数。在http1.1的持久连接,多个请求建立一个TCP连接,按顺序返回,只能串行执行。所以1. 浏览器(谷歌)为每个域名最多同时维护 6 个 TCP 持久连接;但是http2一个域名只使用一个 TCP 长连接来传输数据,所以可以同时发送多个请求。
控制并发请求的必要性:
尽管HTTP/2已经允许并发请求,并且服务器和浏览器会在合适的情况下自动进行优化,但前端仍然需要关注并控制并发请求的数量,主要原因如下:
- 过多的请求同时进行时,可能会导致网络阻塞和资源竞争,从而降低页面加载速度和性能。通过在前端控制并发请求数量,可以确保页面中的关键资源优先加载,提高用户体验。
- 带宽限制: 尽管HTTP/2允许多路复用,但在某些情况下,客户端的带宽可能仍然是有限的。当并发请求过多时,可能会导致带宽过载,导致性能下降,甚至出现请求超时等问题。
- 服务器资源: 虽然HTTP/2减少了服务器和客户端之间的连接数量,但服务器仍然需要分配资源来处理请求。在高并发的情况下,服务器可能会面临负载过重的风险。因此,通过控制并发请求,可以避免服务器过载和性能下降。
- 请求优先级: HTTP/2允许为每个请求设置优先级,以指定请求的重要性。在某些情况下,一些请求可能比其他请求更重要。通过限制并发请求,可以更好地控制请求的优先级,确保重要的请求得到更快的响应。
因此,尽管HTTP/2已经改进了并发请求的处理能力,但前端仍然需要关注并控制并发请求数量,以确保最佳的性能和用户体验。在实际开发中,可以使用适当的技术和工具来限制并发请求,比如合并资源、延迟加载等,以提高网页的性能和加载速度。