Skip to content

Instantly share code, notes, and snippets.

@crazy4groovy
Last active March 10, 2023 00:08
Show Gist options
  • Save crazy4groovy/6dceb9e0ae76ae2f988fa0fe7b707d6e to your computer and use it in GitHub Desktop.
Save crazy4groovy/6dceb9e0ae76ae2f988fa0fe7b707d6e to your computer and use it in GitHub Desktop.
a throttled file downloader (Deno)
import { Timeout, TimeoutError } from "https://deno.land/x/timeout/mod.ts";
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
function newThrottler({ isBusy, lock, unlock, waitMs, size }) {
async function throttler(cb, ...args) {
size(1);
await Promise.resolve();
while (!!isBusy()) {
await delay(waitMs()); // waits in event loop queue, until it interrupts for another attempt!
}
lock();
// ... DO ALL WORK for result
const result = await cb.call(this, ...args);
unlock();
size(-1);
return result;
}
throttler.size = () => size(); // read-only
throttler.isBusy = isBusy;
return throttler;
}
const throttler = (threads: number) => newThrottler((function(){
let size = 0;
let semaphore = 0;
return {
isBusy: () => (semaphore >= threads),
lock: () => (semaphore += 1),
unlock: () => (semaphore -= 1),
waitMs: () => 1000 + (1000 * Math.random()),
size: (n) => n ? (size += n) : size,
};
})());
export const createDownloadThrottled =
(threads: number) => {
const thr = throttler(threads);
function dl(...args) { thr(download, ...args); };
Object.entries(thr).forEach(([k, v]) => { dl[k] = v; });
return dl;
}
// Eg. const dl = createDownloadThrottled(4);
// dl('https://a.com/1.jpg', '~/imgs/1.jpg', myHeaders);
// console.log(dl.size());
// console.log(dl.isBusy());
export async function download(
source: string,
destination: string,
headers: any = {},
): Promise<boolean> {
try {
const req = fetch(source, { headers });
const response = await Timeout.race([req], headers.timeout || 9000);
const blob = await response.blob();
const data = new Uint8Array(await blob.arrayBuffer());
const file = await Deno.create(destination);
await Deno.writeAll(file, data);
Deno.close(file.rid);
return true;
} catch(err) {
if (err instanceof TimeoutError) {
console.error(`ERROR Timed out; skipping: ${source}`);
return false;
}
console.error("ERROR while dl'ing:", err.message);
console.error("---RETRYING...");
await delay(1000);
return download(source, destination, headers);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment