Skip to content

Instantly share code, notes, and snippets.

@apostolos
Created October 4, 2017 17:03
Show Gist options
  • Save apostolos/0cde25a6afbf3fe56320361c92a3559b to your computer and use it in GitHub Desktop.
Save apostolos/0cde25a6afbf3fe56320361c92a3559b to your computer and use it in GitHub Desktop.
function downloadFiles(files /*: Array<string> */) {
// Do not fetch more than 8 files at a time
const PARALLEL_DOWNLOADS = Math.min(8, files.length);
// Create an Iterator from the array, this is our download queue
const queue = files[Symbol.iterator]();
// Channels are our download workers. As soon as a channel finishes downloading a file,
// it begins fetching another file from the queue. Best effort
const channels = [];
// Create only PARALLEL_DOWNLOADS number of channels
for (let i = 0; i < PARALLEL_DOWNLOADS; i += 1) {
channels.push(
// Channel is "open" until this promise resolves/rejects
new Promise(async (resolve, reject) => {
const blobs = [];
// Pick the next() available URL from our queue (@@iterator)
// This is safe because JS is single-threaded
for (let url of queue) {
try {
blobs.push(
// Stop execution until this file downloads
await fetchFile(url)
);
} catch(e) {
reject(e);
}
}
// We are out of URLs to download, "close" the channel
resolve(blobs);
})
);
}
// This is our latch,
// prevents returning control until all channels are closed.
return Promise.all(channels);
}
// Example async work
async function fetchFile(url) {
const response = await fetch(url, {
method: 'GET'
});
if (response.status !== 200) {
throw response.statusText;
}
return await response.blob();
}
// USAGE:
(async function() {
try {
// wait until all files have downloaded
const blobArray = await downloadFiles([/* file URLs here */]);
} catch(err) {
alert(err.message);
return;
}
// all done
}())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment