🚀 A Powerful, Production-Ready Queue System for JavaScript & TypeScript
@aplanka/reliable-queue
is a comprehensive task queue management library designed to handle asynchronous operations with guaranteed execution and intelligent failure recovery. Whether you're building a file upload system, processing background jobs, or managing API calls, this library ensures your tasks are executed reliably with built-in retry mechanisms, real-time status tracking, and seamless React integration.
Problem it solves: In modern web applications, handling asynchronous tasks reliably is challenging. Network failures, server timeouts, and temporary errors can cause important operations to fail silently, leaving your users frustrated and your data inconsistent.
Our solution: This library provides a robust queue system that automatically handles failures, retries failed tasks with smart backoff strategies, provides real-time status updates, and ensures no task is lost - even if the user refreshes the page or closes their browser.
Perfect for:
- 📁 File upload systems with retry capabilities
- 📧 Email sending with delivery guarantees
- 🔄 Background job processing
- 🌐 API call management with failure recovery
- 📊 Data synchronization tasks
- 🎨 Image/video processing pipelines
- 💾 Batch operations with progress tracking
Never lose a task again! When tasks fail due to network issues, server errors, or temporary problems, the queue automatically retries them using intelligent strategies.
How it works:
- Failed tasks are automatically rescheduled for retry
- Each retry attempt is tracked and logged
- Smart exponential backoff prevents overwhelming failing services
- Maximum retry limits prevent infinite loops
- Custom retry delays can be configured per use case
Retry Strategies:
- Fixed Delay: Same delay between each retry (e.g., 1s, 1s, 1s)
- Exponential Backoff: Increasing delays (e.g., 1s, 2s, 4s, 8s) to give failing services time to recover
- Capped Backoff: Exponential with maximum delay limit
// Example: Payment processing with smart retry
const paymentQueue = new ReliableQueue({
maxRetries: 3, // Try up to 3 times total
retryDelay: 1000, // Start with 1 second delay
exponentialBackoff: true, // Double delay each retry: 1s → 2s → 4s
maxRetryDelay: 30000 // Never wait more than 30 seconds
});
paymentQueue.setProcessor(async (payment) => {
// This might fail due to network issues, server errors, etc.
const response = await fetch('/api/process-payment', {
method: 'POST',
body: JSON.stringify(payment)
});
if (!response.ok) {
throw new Error(`Payment failed: ${response.status}`);
}
return response.json();
});
// If this fails, it will be retried automatically:
// Attempt 1: Immediate
// Attempt 2: After 1 second
// Attempt 3: After 2 seconds
// Attempt 4: After 4 seconds
// If still fails: Marked as permanently failed
paymentQueue.add({
userId: '123',
amount: 99.99,
currency: 'USD'
});
Real-world benefits:
- 🌐 Network resilience: Handles temporary internet disconnections
- ⚡ Server recovery: Gives overloaded servers time to recover
- 🔧 Maintenance windows: Automatically resumes after server maintenance
- 💪 User experience: Operations complete even with unstable connections
Monitor task progress with detailed status information.
// Track individual task status
const taskId = queue.add({ data: 'example' });
const task = queue.getTask(taskId);
console.log(task.status); // 'pending' | 'processing' | 'completed' | 'failed'
// Get overall queue statistics
const stats = queue.getStats();
console.log(`${stats.pending} pending, ${stats.failed} failed`);
Subscribe to real-time queue events for responsive applications.
queue.on('taskCompleted', (task) => {
console.log(`✅ Task ${task.id} completed successfully`);
});
queue.on('taskFailed', (task, error) => {
console.log(`❌ Task ${task.id} failed: ${error.message}`);
// Send notification, log error, etc.
});
queue.on('queueUpdated', (tasks) => {
updateUI(tasks); // Update your UI in real-time
});
Process multiple tasks simultaneously for better performance.
const queue = new ReliableQueue({
concurrency: 5 // Process up to 5 tasks at the same time
});
// All these tasks can be processed concurrently
for (let i = 0; i < 10; i++) {
queue.add({ fileId: i, action: 'process-image' });
}
Higher priority tasks are processed first.
// Low priority background task
queue.add({ task: 'cleanup' }, { priority: 1 });
// High priority user action
queue.add({ task: 'user-request' }, { priority: 10 });
// Critical system task
queue.add({ task: 'security-check' }, { priority: 100 });
// Tasks will be processed in priority order: security-check → user-request → cleanup
Schedule tasks to run at a specific time in the future.
// Process this task after 5 seconds
queue.add(
{ action: 'send-reminder' },
{ delay: 5000 }
);
// Schedule for specific time
const delayUntilMidnight = new Date().setHours(24, 0, 0, 0) - Date.now();
queue.add(
{ action: 'daily-report' },
{ delay: delayUntilMidnight }
);
Tasks survive page refreshes and browser restarts.
const queue = new ReliableQueue({
persistent: true,
storageKey: 'my-app-queue' // Custom storage key
});
// Add tasks - they'll persist even if user refreshes the page
queue.add({ action: 'upload-file', fileId: '123' });
// After page refresh, tasks are automatically restored
Seamless integration with React applications using custom hooks.
function FileUploader() {
const { tasks, stats, addTask, retryTask } = useReliableQueue({
queueName: 'file-uploads',
processor: async (file) => {
// Upload file logic
}
});
return (
<div>
<div>Uploading: {stats.processing} files</div>
<div>Failed: {stats.failed} files</div>
{tasks.map(task => (
<div key={task.id}>
{task.data.name} - {task.status}
{task.status === 'failed' && (
<button onClick={() => retryTask(task.id)}>Retry</button>
)}
</div>
))}
</div>
);
}
Full TypeScript support with custom data types.
interface EmailTask {
to: string;
subject: string;
body: string;
priority: 'low' | 'high' | 'urgent';
}
const emailQueue = new ReliableQueue<EmailTask>();
// TypeScript enforces correct data structure
emailQueue.add({
to: 'user@example.com',
subject: 'Welcome!',
body: 'Thanks for signing up',
priority: 'high'
});
// Type-safe event handling
emailQueue.on('taskCompleted', (task) => {
// task.data is properly typed as EmailTask
console.log(`Email sent to ${task.data.to}`);
});
Manage multiple independent queues for different purposes.
import { QueueManager } from '@aplanka/reliable-queue';
const manager = QueueManager.getInstance();
// Create specialized queues
const emailQueue = manager.createQueue('emails', {
maxRetries: 5,
concurrency: 3
});
const imageQueue = manager.createQueue('images', {
maxRetries: 2,
concurrency: 2
});
const reportQueue = manager.createQueue('reports', {
maxRetries: 1,
concurrency: 1
});
// Each queue operates independently
emailQueue.add({ type: 'welcome-email' });
imageQueue.add({ type: 'resize-avatar' });
reportQueue.add({ type: 'monthly-summary' });
npm install @aplanka/reliable-queue
import { ReliableQueue, TaskStatus } from '@aplanka/reliable-queue';
// Create a queue with basic configuration
const queue = new ReliableQueue({
maxRetries: 3,
retryDelay: 1000,
exponentialBackoff: true,
});
// Define what happens when tasks are processed
queue.setProcessor(async (data) => {
console.log('Processing task:', data);
// Your task logic here - e.g., API call
const response = await fetch('/api/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error('Processing failed');
}
return await response.json();
});
// Add a simple task
const taskId = queue.add({
message: 'Process this data',
userId: '123'
});
// Add task with options
queue.add(
{ message: 'High priority task' },
{
priority: 10, // Higher number = higher priority
delay: 5000, // Wait 5 seconds before processing
}
);
// Listen for task completion
queue.on('taskCompleted', (task) => {
console.log('✅ Task completed:', task.id);
});
// Listen for task failures
queue.on('taskFailed', (task, error) => {
console.log('❌ Task failed:', task.id, error.message);
});
// Listen for queue updates
queue.on('queueUpdated', (tasks) => {
console.log('📊 Queue now has', tasks.length, 'tasks');
});
// Get queue statistics
const stats = queue.getStats();
console.log(`Total: ${stats.total}, Pending: ${stats.pending}, Failed: ${stats.failed}`);
// Get all tasks
const tasks = queue.getTasks();
// Retry failed tasks
const retriedCount = queue.retryAll();
// Clear completed tasks
const clearedCount = queue.clearCompleted();
import React from 'react';
import { useReliableQueue } from '@aplanka/reliable-queue';
interface MyTask {
id: string;
action: string;
data: any;
}
function TaskManager() {
const {
tasks,
stats,
addTask,
retryTask,
clearFailed,
} = useReliableQueue<MyTask>({
queueName: 'my-tasks',
maxRetries: 3,
processor: async (task) => {
// Process your task
await fetch('/api/process', {
method: 'POST',
body: JSON.stringify(task),
});
},
});
return (
<div>
<h2>Task Manager</h2>
{/* Queue Stats */}
<div>
<span>Total: {stats.total}</span>
<span>Pending: {stats.pending}</span>
<span>Failed: {stats.failed}</span>
</div>
{/* Add Task Button */}
<button onClick={() => addTask({
id: Date.now().toString(),
action: 'process-data',
data: { message: 'Hello World' }
})}>
Add Task
</button>
{/* Task List */}
<ul>
{tasks.map(task => (
<li key={task.id}>
<span>{task.status}</span> - {task.data.action}
{task.status === 'failed' && (
<button onClick={() => retryTask(task.id)}>Retry</button>
)}
</li>
))}
</ul>
</div>
);
}
interface FileTask {
file: File;
uploadUrl: string;
}
const uploadQueue = new ReliableQueue<FileTask>({
concurrency: 3, // Upload 3 files at once
});
uploadQueue.setProcessor(async ({ file, uploadUrl }) => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(uploadUrl, {
method: 'POST',
body: formData,
});
if (!response.ok) throw new Error('Upload failed');
});
// Add files to upload
files.forEach(file => {
uploadQueue.add({ file, uploadUrl: '/api/upload' });
});
interface JobTask {
jobType: 'email' | 'report' | 'export';
payload: any;
}
const jobQueue = new ReliableQueue<JobTask>({
maxRetries: 5,
persistent: true, // Survive page refreshes
});
jobQueue.setProcessor(async ({ jobType, payload }) => {
await fetch(`/api/jobs/${jobType}`, {
method: 'POST',
body: JSON.stringify(payload),
});
});
// Process different job types
jobQueue.add({ jobType: 'email', payload: { userId: '123' } });
jobQueue.add({ jobType: 'report', payload: { reportId: 'monthly' } });
interface QueueConfig {
maxRetries?: number; // Default: 3
retryDelay?: number; // Default: 1000ms
exponentialBackoff?: boolean; // Default: true
maxRetryDelay?: number; // Default: 30000ms
concurrency?: number; // Default: 1
persistent?: boolean; // Default: false
storageKey?: string; // Default: 'reliable-queue'
}
pending
- Task is waiting to be processedprocessing
- Task is currently being processedcompleted
- Task finished successfullyfailed
- Task failed after all retry attempts
add(data, options?)
- Add task to queueremove(taskId)
- Remove task from queueclear()
- Remove all non-processing tasksgetTasks()
- Get all tasksgetStats()
- Get queue statistics
retry(taskId)
- Retry specific failed taskretryAll()
- Retry all failed tasksclearCompleted()
- Remove completed tasksclearFailed()
- Remove failed tasks
taskAdded
- When task is addedtaskStarted
- When task starts processingtaskCompleted
- When task completes successfullytaskFailed
- When task fails permanentlytaskRetried
- When task is retriedqueueUpdated
- When queue state changes
Full TypeScript support with generic types:
interface MyData {
id: number;
message: string;
}
const queue = new ReliableQueue<MyData>();
// Type-safe task data
queue.add({ id: 1, message: 'Hello' });
// Type-safe event handlers
queue.on('taskCompleted', (task) => {
// task.data is typed as MyData
console.log(task.data.message);
});
MIT © Pasindu Lanka