前提:
CHROME浏览器(http1.1)
①同一域名下,同一GET请求的并发数是1,也就是说上一个请求结束,才会执行下一个请求,否则置入队列等待发送;
②同一域名下,不同GET/POST请求的并发数量是6。当发送的请求数量达到6个,并且都没有得到响应时,后面的请求会置入队列等待发送。
问题
多个 raster 瓦片(256*256)图层,不同域名,在加载时会阻塞
代码实现(核心)
let queue, numFetch;
export const resetFetchQueue = () => {
queue = [];
numFetch = 0;
};
resetFetchQueue();
function fetch (p, callback) {
let complete = false;
let aborted = false;
const controller = new window.AbortController();
setTimeout(() => {
if (!aborted) {
callback();
}
}, Math.random() * 10);
return {
cancel: () => {
aborted = true;
if (!complete) controller.abort();
},
};
}
function run (params, callback) {
if (numFetch >= config.MAX_PARALLEL_IMAGE_REQUESTS) {
const queued = {
params,
callback,
cancelled: false,
cancel() { this.cancelled = true; }
};
queue.push(queued);
return queued;
}
numFetch++;
let advanced = false;
const advanceFetchQueue = () => {
if (advanced) return;
advanced = true;
numFetch--;
while (queue.length && numFetch < config.MAX_PARALLEL_IMAGE_REQUESTS) { // eslint-disable-line
const request = queue.shift();
const { params, callback, cancelled } = request;
if (!cancelled) {
request.cancel = fetch(params, callback).cancel;
}
}
};
const request = fetch(params, (err: any, data: any) => {
advanceFetchQueue();
if (err) {
callback(err);
} else if (data) {
callback(null, data);
}
});
return {
cancel: () => {
request.cancel();
advanceFetchQueue();
}
};
}
当我们调用 run 的时候(对应 mapbox 代码中的getImage 函数)发生了什么:
- 当正在请求的数量大于等于设置的最大并发数时,我们不会直接调用真实的请求,而是将当前所需的请求
push到队列中,返回当前队列的标识queued。 - 如果正在请求的数量小于设置的最大并发数时,首先调用的
numFetch计数自增,说明当前并发未满。 - 发出真实请求。
- 真实请求不论成功和失败,执行请求队列;
- 当队列不为空并且当前请求数量小于设置的最大并发数时,循环取出队列的第一个请求执行。
这样的实现一般是基于一下几种原因:
- 控制并发量,可以避免同时加载大量瓦片,从而减轻服务器和客户端的负担,并提高网站的性能。
- 未完成的无用请求可以取消(因为地图我们每次缩放和平移都会请求大量的瓦片,当我们快速的从 0 级放大到 10 级,那么中间层级的大部分瓦片的请求队列都是不必要的,那么可以丢弃或者取消请求)
在以上伪代码实现中MAX_PARALLEL_IMAGE_REQUESTS的默认值是 16,也就是说它允许的并行的图片请求最大为 16,我们也可以通过 map.maxParallelImageRequests=number来更改默认值。
还需要注意的是我们文前提到同一域名下(http1.1)的浏览器最大并发限制为 6,如果我们使用了多个不同域名的影像瓦片图层(256*256)mapbox-gl内部还是限制了最大并发数,这种情况下无法充分利用浏览器的资源并发。