UNPKG

react-native-background-task-manager

Version:

Advanced React Native background task manager with foreground services, scheduling, and geolocation support for Android

294 lines (271 loc) 7.41 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TaskManagerClass = exports.TaskManager = void 0; var _reactNative = require("react-native"); class TaskManagerClass { tasks = new Map(); taskRunner = null; isRunning = false; samplingInterval = 500; // ms /** * Generate a unique task ID */ generateTaskId() { return "task_" + Math.random().toString(36).substring(2, 14); } /** * Add a new task to the task manager */ addTask(taskFn, config) { const taskId = config.taskId ?? this.generateTaskId(); const task = { taskId, task: taskFn, config: { ...config, priority: config.priority ?? 'normal', retryCount: config.retryCount ?? 0, timeout: config.timeout ?? 30000 }, nextExecutionTime: Date.now() + (config.delay ?? 0), executionCount: 0, status: 'pending', retryAttempts: 0 }; this.tasks.set(taskId, task); this.startTaskRunner(); return taskId; } /** * Remove a task from the task manager */ removeTask(taskId) { this.tasks.delete(taskId); if (this.tasks.size === 0) { this.stopTaskRunner(); } } /** * Update an existing task */ updateTask(taskId, taskFn, config) { const existingTask = this.tasks.get(taskId); if (!existingTask) { // If task doesn't exist, add it this.addTask(taskFn, { ...config, taskId }); return; } const updatedTask = { ...existingTask, task: taskFn, config: { ...config, priority: config.priority ?? 'normal', retryCount: config.retryCount ?? 0, timeout: config.timeout ?? 30000 }, nextExecutionTime: Date.now() + (config.delay ?? 0), status: 'pending' }; this.tasks.set(taskId, updatedTask); } /** * Pause a task */ pauseTask(taskId) { const task = this.tasks.get(taskId); if (task && task.status !== 'paused') { task.status = 'paused'; this.tasks.set(taskId, task); } } /** * Resume a paused task */ resumeTask(taskId) { const task = this.tasks.get(taskId); if (task && task.status === 'paused') { task.status = 'pending'; task.nextExecutionTime = Date.now() + (task.config.delay ?? 0); this.tasks.set(taskId, task); } } /** * Check if a task is currently running */ isTaskRunning(taskId) { const task = this.tasks.get(taskId); return task ? task.status === 'running' : false; } /** * Get all tasks with their status */ getAllTasks() { const result = {}; for (const [taskId, task] of this.tasks) { result[taskId] = { taskId: task.taskId, isRunning: task.status === 'running', executionCount: task.executionCount, ...(task.lastExecutionTime !== undefined && { lastExecutionTime: task.lastExecutionTime }), ...(task.nextExecutionTime !== undefined && { nextExecutionTime: task.nextExecutionTime }), status: task.status }; } return result; } /** * Get status of a specific task */ getTaskStatus(taskId) { const task = this.tasks.get(taskId); if (!task) return null; return { taskId: task.taskId, isRunning: task.status === 'running', executionCount: task.executionCount, ...(task.lastExecutionTime !== undefined && { lastExecutionTime: task.lastExecutionTime }), ...(task.nextExecutionTime !== undefined && { nextExecutionTime: task.nextExecutionTime }), status: task.status }; } /** * Remove all tasks */ removeAllTasks() { this.tasks.clear(); this.stopTaskRunner(); } /** * Start the task runner */ startTaskRunner() { if (this.isRunning) return; this.isRunning = true; this.taskRunner = setInterval(() => { this.executeTasksCycle(); }, this.samplingInterval); } /** * Stop the task runner */ stopTaskRunner() { if (this.taskRunner) { clearInterval(this.taskRunner); this.taskRunner = null; } this.isRunning = false; } /** * Execute tasks that are ready to run */ async executeTasksCycle() { if (!this.isRunning) return; const now = Date.now(); const readyTasks = Array.from(this.tasks.values()).filter(task => this.shouldExecuteTask(task, now)).sort((a, b) => this.getTaskPriority(a.config.priority) - this.getTaskPriority(b.config.priority)); const promises = readyTasks.map(task => this.executeTask(task)); await Promise.all(promises.map(p => p.catch(e => console.error('Task execution error:', e)))); } /** * Check if a task should be executed */ shouldExecuteTask(task, now) { return task.status === 'pending' && now >= task.nextExecutionTime; } /** * Get numeric priority value for sorting */ getTaskPriority(priority) { switch (priority) { case 'high': return 1; case 'normal': return 2; case 'low': return 3; default: return 2; } } /** * Execute a single task */ async executeTask(task) { task.status = 'running'; task.lastExecutionTime = Date.now(); task.executionCount++; this.tasks.set(task.taskId, task); try { // Execute task with timeout const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Task timeout')), task.config.timeout); }); const taskPromise = Promise.resolve(task.task()); await Promise.race([taskPromise, timeoutPromise]); // Task succeeded task.status = task.config.onLoop ? 'pending' : 'completed'; if (task.config.onLoop) { task.nextExecutionTime = Date.now() + task.config.delay; } else { // Remove one-time task after completion this.tasks.delete(task.taskId); } // Call success callback if (task.config.onSuccess) { task.config.onSuccess(); } } catch (error) { // Task failed task.retryAttempts++; if (task.retryAttempts <= (task.config.retryCount ?? 0)) { // Retry the task task.status = 'pending'; task.nextExecutionTime = Date.now() + task.config.delay; } else { // Max retries reached task.status = 'failed'; // Call error callback if (task.config.onError) { task.config.onError(error); } } } this.tasks.set(task.taskId, task); } /** * Register a headless task for background execution */ registerForegroundTask(taskName, task) { _reactNative.AppRegistry.registerHeadlessTask(taskName, () => task); } /** * Get task manager statistics */ getStats() { const tasks = Array.from(this.tasks.values()); return { totalTasks: tasks.length, runningTasks: tasks.filter(t => t.status === 'running').length, pendingTasks: tasks.filter(t => t.status === 'pending').length, completedTasks: tasks.filter(t => t.status === 'completed').length, failedTasks: tasks.filter(t => t.status === 'failed').length }; } } // Export singleton instance exports.TaskManagerClass = TaskManagerClass; const TaskManager = exports.TaskManager = new TaskManagerClass(); //# sourceMappingURL=TaskManager.js.map