UNPKG

queue-manager-pro

Version:

A flexible, TypeScript-first queue/task manager with pluggable backends ,dynamic persistence storage and event hooks.

99 lines 3.62 kB
import { TaskMaxRetriesExceededError, TaskProcessingTimeoutError } from '../util/errors.js'; export class QueueWorker { queueManager; workerActive = false; workerPromise; logger; constructor(queueManager, logger) { this.queueManager = queueManager; this.logger = logger; } async startWorker(concurrency = 1) { this.log('info', `Starting ${concurrency} workers`); this.workerActive = true; const workers = []; for (let i = 0; i < concurrency; i++) { workers.push(this.queueWorker()); } this.workerPromise = Promise.all(workers); } async stopWorker() { this.log('info', 'Worker stopping...'); this.workerActive = false; await this.workerPromise; this.log('info', 'Worker stopped'); } async processTaskWithTimeout(task) { let timeoutId; const timeoutPromise = new Promise((_, reject) => { timeoutId = setTimeout(() => reject(new TaskProcessingTimeoutError()), task.maxProcessingTime + 1000); }); try { const promise = this.processTask(task); return await Promise.race([promise, timeoutPromise]); } catch (err) { if (err instanceof TaskProcessingTimeoutError) { if (task.retryCount <= task.maxRetries) { task.retryCount++; this.queueManager.emit('taskRetried', task); clearTimeout(timeoutId); this.log('error', `${task.id}:`, err); return await this.processTaskWithTimeout(task); } else { this.log('error', `Task ${task.id} failed:`, err); throw new TaskMaxRetriesExceededError(task.id, err.message); } } else { throw err; // Re-throw unexpected errors } } finally { clearTimeout(timeoutId); } } async queueWorker() { while (this.workerActive) { const task = await this.queueManager['repository'].dequeue(); if (task) { try { await this.processTaskWithTimeout(task); } catch (err) { await this.handleTaskError(task, err); } } else { await new Promise(res => setTimeout(res, this.queueManager['delay'])); } } } async processTask(task) { this.queueManager.emit('taskStarted', task); const handler = this.queueManager['registry'].get(task.handler); await handler.fn(task.payload); await this.queueManager['repository'].updateTask(task.id, { status: 'done' }); this.queueManager.emit('taskCompleted', task); } async handleTaskError(task, err) { this.log('error', `Task ${task.id} failed:`, err); const log = err?.message?.toString() || 'Unknown error'; await this.queueManager['repository'].updateTask(task.id, { status: 'failed', log }); const emitError = err instanceof Error ? err : new Error(String(err)); if (this.queueManager.crashOnWorkerError) { this.workerActive = false; throw emitError; } else { this.queueManager.emit('taskFailed', task, emitError); } } log(level, ...args) { if (this.logger?.[level]) { this.logger[level](...args); } } } //# sourceMappingURL=queueWorker.js.map