请求池
本文最后更新于:3 个月前
问题
有8个图片资源的url,已经存储在数组urls中。
urls类似于[‘https://image1.png', ‘https://image2.png', ….]
而且已经有一个函数function loadImg,输入一个url链接,返回一个Promise,该Promise在图片下载完成的时候resolve,下载失败则reject。
但有一个要求,任何时刻同时下载的链接数量不可以超过3个。
请写一段代码实现这个需求,要求尽可能快速地将所有图片下载完成。
var urls = [
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting1.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting2.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting3.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting4.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting5.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn6.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn7.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn8.png",
];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
console.log("一张图片加载完成");
resolve(img);
};
img.onerror = function() {
reject(new Error('Could not load image at' + url));
};
img.src = url;
});
想法
在实际项目中,经常会出现问题中的情况,我们需要请求很多资源或者进行很多请求,但是并发数过多会使得其他请求进入排队状态,从而影响页面的展示。这里我们就有必要使用一个限制请求的请求池维护请求。通过维护一个请求队列,在执行完一个请求后及时调用新的请求,维持时刻都有请求正在进行,并且最多只有上限数量的请求
代码
最开始想到的方法,比较复杂
function createRequest({ list = [], pool = 5 }) {
//TODO
let promises = [];
function queue(index = 0) {
//执行到最后一个边界
if (index === list.length) {
return Promise.all(promises);
}
//取出一个请求
const promise = list[index]();
//执行进队列
promises.push(doRequest(promise, index));
//如果进入队列的长度到了上限,拿到队列中最快的请求
let race = Promise.resolve();
if (promises.length >= pool) {
race = Promise.race(promises);
}
//在最快执行完的promise中调用下一次请求添加入队列
return race.then(() => queue(index + 1));
}
//做一次请求
function doRequest(promise, index) {
const p = new Promise(async (re, rj) => {
try {
await promise;
re(index);
} catch (e) {
console.log(index);
rj(e);
}
}).then(() => {
//执行完删除队列里的自己
let index = promises.indexOf(p);
index !== -1 && promises.splice(index, 1);
});
return p;
}
return queue();
}
let p = function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log("1000");
resolve(1000);
}, 1000);
});
};
let p1 = function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log("7000");
resolve(7000);
}, 7000);
});
};
let p2 = function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log("3000");
reject(3000);
}, 3000);
});
};
let p3 = function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log("4000");
resolve(4000);
}, 4000);
});
};
let p4 = function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log("5000");
resolve(5000);
}, 3000);
});
};
let p5 = function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log("6000");
resolve(6000);
}, 3000);
});
};
console.time("time");
createRequest({ list: [p, p1, p2, p3, p4, p5], pool: 4 }).then((res) => {
console.timeEnd("time");
console.log(res);
});
urls使用的网络上的图片,更加直观
let urls = [
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting1.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting2.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting3.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting4.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting5.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn6.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn7.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn8.png",
];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function () {
console.log("一张图片加载完成");
resolve(img);
};
img.onerror = function () {
reject(new Error('Could not load image at' + url));
};
img.src = url;
img.width = '100'
img.height = '200'
});
}
function createRequest(urls,limit) {
let results = [];
//先拿出最大数量的请求放进请求池
let promises = urls.splice(0, limit).map((element, index) => {
return loadImg(element).then(res => {
results.push(res)
return index;
},res=>{
results.push(res)
return index;
})
});
let queue = i=>{
//使用race去拿出最快完成的一项
return Promise.race(promises).then(res => {
//替换掉请求池中完成的一项
promises[res] = loadImg(urls[i]).then(_=> {
results.push(_)
return res;
},_=>{
results.push(_)
return res;
})
},res=>{
//替换掉请求池中完成的一项
promises[res] = loadImg(urls[i]).then(_=> {
results.push(_)
return res;
},_=>{
results.push(_)
return res;
})
}).then(_=>{
//如果排队的请求还有,进入下一个请求的操作
if (i < urls.length - 1) {
return queue(i + 1)
}else{
//请求没有了,就使用all把最后三个直接全部结束
return Promise.allSettled(promises).then(_=>result);
}
});
}
return queue(0)
}
createRequest(urls,3).then(res=>{
console.log('请求结束',res)
})
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!