node-downloader-manager
Version:
node-downloader-manager is a simple yet powerful package manager-like download manager built with NodeJs. It allows you to download files sequentially or with a queue-based approach, handling retries and concurrency limits efficiently.
105 lines (104 loc) • 3.59 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class Queue {
heap = [];
activeCount = 0;
concurrencyLimit;
maxRetries;
log;
backOff;
constructor(concurrencyLimit, maxRetries, consoleLog, backOff = false) {
this.concurrencyLimit = concurrencyLimit;
this.maxRetries = maxRetries;
this.log = consoleLog;
this.backOff = backOff;
}
logger(message, type = "info") {
if (this.log) {
console[type](`[${process.pid}] : [${new Date().toLocaleString()}] : `, message);
}
}
calculateBackoff(attempt) {
const baseDelay = 1000;
const maxDelay = 30000;
return Math.min(baseDelay * 2 ** attempt, maxDelay);
}
heapifyUp(index) {
const task = this.heap[index];
while (index > 0) {
const parentIndex = Math.floor((index - 1) / 2);
if (parentIndex < 0 || parentIndex >= this.heap.length)
break;
if (this.heap[parentIndex] &&
this.heap[parentIndex].priority >= task.priority)
break;
this.heap[index] = this.heap[parentIndex];
index = parentIndex;
}
this.heap[index] = task;
}
enqueue(task) {
this.heap.push(task);
this.heapifyUp(this.heap.length - 1);
this.runNext();
}
heapifyDown(index) {
const length = this.heap.length;
while (true) {
const leftChildIndex = 2 * index + 1;
const rightChildIndex = 2 * index + 2;
let largestIndex = index;
if (leftChildIndex < length &&
this.heap[leftChildIndex].priority > this.heap[largestIndex].priority) {
largestIndex = leftChildIndex;
}
if (rightChildIndex < length &&
this.heap[rightChildIndex].priority > this.heap[largestIndex].priority) {
largestIndex = rightChildIndex;
}
if (largestIndex === index)
break;
[this.heap[index], this.heap[largestIndex]] = [
this.heap[largestIndex],
this.heap[index],
];
index = largestIndex;
}
}
async runNext() {
if (this.activeCount >= this.concurrencyLimit || this.heap.length === 0) {
return;
}
const task = this.heap[0];
if (!task)
return;
this.heap[0] = this.heap[this.heap.length - 1];
this.heap.pop();
this.heapifyDown(0);
this.activeCount++;
try {
await task.action();
this.logger(`Task ${task.id} completed successfully.`);
}
catch (err) {
this.logger(`Task ${task.id} failed: ${err}`, "error");
if (task.retries < this.maxRetries) {
this.logger(`Retrying task ${task.id}...`);
if (this.backOff) {
const backoffDelay = this.calculateBackoff(task.retries);
this.logger(`Task ${task.id} - Retrying in ${backoffDelay / 1000} seconds...`);
await new Promise((resolve) => setTimeout(resolve, backoffDelay));
}
this.enqueue({ ...task, retries: task.retries + 1 });
}
else {
this.logger(`Task ${task.id} exceeded max retries.`, "warn");
}
}
finally {
this.activeCount--;
this.runNext();
}
}
}
exports.default = Queue;