我有1个数组,像这样的数千个链接图像
let imageList = ["http://img1.jpg", "http://img2.jpg", ...];
我想遍历imageList,并在增加索引20次(n次)后延迟,例如
for(let i = 0; i <= imageList.length; i+=20){
// with i from 0 -> 20
// do download image from server
downloadImages(0,20) // [start, end]
// delay 5s to prevent server timeout because request many times
// with i from 20 -> 40
// do download image from server
downloadImages(20,40)
// continue delay 5s
// .... try to finish
}
尝试像这样的东西:
const imageList = ['***'];
downloadImages(imageList)
.then(/* done */)
.catch(/* error */);
async function downloadImages(images) {
for(let i = 0; i + 20 <= imageList.length; i += 20){
const n20images = imageList.slice(i, i + 20);
await fetchImages(n20images);
await delay(5);
}
}
function fetchImages(images) {
return Promise.all(
images.map(image => /* fetch(image) or smth else */)
)
}
function delay(seconds) {
return new Promise(resolve => setTimeout(resolve, seconds * 1000))
}
在这种情况下,我使用async / await,来自lodash的of和块。它将以20组为单位发出不淹没服务器的请求
let i = 0
const imageListChunks = _.chunk(imageList, 20)
for await (const chunk of imageListChunks){
const chunkPromises = downloadImage(0 + i*20, 20 + i*20)
const chunkResp = await Promise.all(chunkPromises)
i = i + 1
}
如果需要更多的延迟来让服务器喘口气,可以添加setTimeout以及另一个等待时间以减慢它的速度
您可以使用async
和setTimeout
来实现:
let downloadImage = async url => {
console.log(`Downloading ${url}`);
// Simulate a download delay
return new Promise(r => setTimeout(r, 100 + Math.floor(Math.random() * 500)));
}
let downloadAllImages = async (imageUrls, chunkSize=20, delayMs=2000) => {
for (let i = 0; i < imageUrls.length; i += chunkSize) {
console.log(`Downloading chunk [${i}, ${i + 20 - 1}]`);
// This `chunk` is a subset of `imageUrls`: the ones to be downloaded next
let chunk = imageUrls.slice(i, i + 20);
// Call `downloadImage` on each item in the chunk; wait for all downloads to finish
await Promise.all(chunk.map(url => downloadImage(url)));
// Unless this is the last chunk, wait `delayMs` milliseconds before continuing
// (Note: this step may be poor practice! See explanation at bottom of this answer)
if ((i + chunkSize) < imageUrls.length) await new Promise(r => setTimeout(r, delayMs));
}
};
// Create an array of example urls
let imageUrls = [ ...new Array(100) ].map((v, n) => `http://example.com/image${n}.jpg`);
// Call `downloadAllImages`
downloadAllImages(imageUrls)
// Use `.then` to capture the moment when all images have finished downloading
.then(() => console.log(`Finished downloading ${imageUrls.length} images!`));
注意,如果正确实现了downloadImage
,以便它返回一个承诺,该承诺将在下载图像时解决该问题,最好的做法是放弃超时。超时是一种启发式方法,可确保一次不会运行太多请求,但是如果您对请求完成的时间有清晰的了解,则可以在开始下一批之前简单地等待一批请求完成。
(有一个更有效的设计可供考虑(供您进一步研究)。为了理解,让我们考虑一下这种当前方法的问题(我将其称为“批处理”方法)。在当前批次完成之前,批次方法无法开始新批次。假设有20张图片,其中1张图片在1毫秒内下载,其中18张图片在5毫秒内下载,但最终图片需要10秒钟以上的下载时间;即使该系统应该具有一次下载20张图像的带宽,但最终只花费一个请求就花费了整整10秒钟的时间。一种更有效的设计(我们可以将其称为“最大带宽方法”)将保持20个进行中请求的队列,并且每次这些请求中的一个完成新请求时,都会立即开始。想象一下,第一张图片在1毫秒内下载完毕;当它完成并且只有19个请求正在进行时,“最大带宽方法”可以立即开始一个新请求,而无需等待其他19个请求完成。
设置一些偏移
let offset = 0
for (let i = offset; i <= imageList.length; i += 20) {
downloadImage(offset, offset + 20)
offset += 20
}
您可以使用modulus operator。
let imageList = ["http://img1.jpg", "http://img2.jpg", ...];
for(let i = 0; i <= imageList.length; i++){
if (i % n == 0) //n is number of images to start delay
START_YOUR_DELAY_HERE
downloadImage(20); //20 is number of images you want to download
}