UNPKG

code-graph-generator

Version:

Generate Json Object of code that can be used to generate code-graphs for JavaScript/TypeScript/Range projects

168 lines 6.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TaskQueue = void 0; /** * Task queue for controlled concurrency with error handling and timeout protection */ class TaskQueue { /** * Creates a new TaskQueue * @param concurrency Maximum number of concurrent tasks * @param options Additional options for the queue */ constructor(concurrency, options) { this.errors = []; this.isShutdown = false; this.taskTimeout = 0; // 0 means no timeout this.concurrency = Math.max(1, concurrency); this.running = 0; this.taskQueue = []; this.completionPromises = []; if (options?.taskTimeout) { this.taskTimeout = options.taskTimeout; } } /** * Add a task to the queue and get a promise that resolves when it completes * @param task Function to execute * @param priority If true, add to front of queue instead of back * @returns Promise that resolves when the task completes */ push(task, priority = false) { if (this.isShutdown) { return Promise.reject(new Error('Task queue has been shut down')); } return new Promise((resolve, reject) => { const taskItem = { task, resolve, reject }; if (priority) { this.taskQueue.unshift(taskItem); } else { this.taskQueue.push(taskItem); } // Run next tasks in a separate cycle to avoid reentrancy issues setTimeout(() => this.runNext(), 0); }); } /** * Run the next task if there's capacity */ runNext() { if (this.isShutdown) return; // Process as many tasks as we have capacity for while (this.running < this.concurrency && this.taskQueue.length > 0) { const taskItem = this.taskQueue.shift(); if (!taskItem) continue; this.running++; let timeoutId; // Set up timeout if configured if (this.taskTimeout > 0) { timeoutId = setTimeout(() => { const error = new Error(`Task timed out after ${this.taskTimeout}ms`); this.errors.push(error); taskItem.reject(error); }, this.taskTimeout); taskItem.timeout = timeoutId; } // Execute the task Promise.resolve().then(() => taskItem.task()) .then(() => { if (timeoutId) clearTimeout(timeoutId); taskItem.resolve(); }) .catch(error => { if (timeoutId) clearTimeout(timeoutId); this.errors.push(error); taskItem.reject(error); }) .finally(() => { this.running--; this.runNext(); // Check if all tasks are done this.checkCompletion(); }); } } /** * Check if all tasks are complete and resolve/reject completion promises */ checkCompletion() { if (this.running === 0 && this.taskQueue.length === 0) { // Copy before resolving in case new ones are added during resolution const promises = [...this.completionPromises]; this.completionPromises = []; if (this.errors.length > 0) { // Create a combined error with all error messages const errorMessage = this.errors.length === 1 ? this.errors[0].message : `Multiple errors occurred (${this.errors.length}): ${this.errors.map(e => e.message).join('; ')}`; const combinedError = new Error(errorMessage); // Store original errors as a property (won't show in stack trace but can be inspected) combinedError.errors = [...this.errors]; promises.forEach(({ reject }) => reject(combinedError)); } else { promises.forEach(({ resolve }) => resolve()); } this.errors = []; } } /** * Wait for all current tasks to complete * @returns Promise that resolves when all tasks are done, or rejects if any fail */ waitForAll() { if (this.running === 0 && this.taskQueue.length === 0) { if (this.errors.length > 0) { const errorMessage = this.errors.length === 1 ? this.errors[0].message : `Multiple errors occurred (${this.errors.length}): ${this.errors.map(e => e.message).join('; ')}`; const combinedError = new Error(errorMessage); combinedError.errors = [...this.errors]; this.errors = []; return Promise.reject(combinedError); } return Promise.resolve(); } return new Promise((resolve, reject) => { this.completionPromises.push({ resolve, reject }); }); } /** * Get the number of running tasks */ getRunningCount() { return this.running; } /** * Get the number of pending tasks */ getPendingCount() { return this.taskQueue.length; } /** * Shut down the queue, rejecting any pending tasks */ shutdown() { this.isShutdown = true; // Reject all pending tasks const error = new Error('Task queue was shut down'); this.taskQueue.forEach(taskItem => { if (taskItem.timeout) clearTimeout(taskItem.timeout); taskItem.reject(error); }); this.taskQueue = []; // Resolve completion promises if no tasks are running if (this.running === 0) { this.completionPromises.forEach(({ resolve }) => resolve()); this.completionPromises = []; } } } exports.TaskQueue = TaskQueue; //# sourceMappingURL=queue.js.map