Async Event Loader
A Dart package for managing and processing asynchronous events sequentially with support for retry logic, error handling, timeout management, and real-time status tracking.
Features ✨
- ➡️ Sequential Processing: Events are processed one at a time in order
- 🔁 Retry Logic: Automatic retry with configurable retry limit
- 🛡️ Error Handling: Configurable error handling with skip-on-error option
- ⏰ Timeout Management: Both controller-level and event-level timeouts
- 📊 Status Tracking: Real-time status updates via streams
- ⏯️ Pause/Resume: Ability to pause and resume processing
- 📈 Progress Tracking: Built-in progress percentage calculation
Installation 💻
Install via dart pub add:
dart pub add async_event_loader
Or add it to your pubspec.yaml:
dependencies:
async_event_loader: ^0.0.2
Quick Start 🚀
Basic Usage
import 'package:async_event_loader/async_event_loader.dart';
// Create events
final events = [
AsyncEvent(
label: 'Load Data',
action: () async {
final data = await fetchData();
return data;
},
onSuccess: () => print('Data loaded successfully'),
onError: (error) => print('Error: $error'),
),
AsyncEvent(
label: 'Process Data',
action: () async {
await processData();
},
onSuccess: () => print('Data processed'),
),
];
// Create controller
final controller = AsyncEventLoaderController(
events: events,
retryLimit: 3,
skipOnError: false,
);
// Listen to status updates
controller.eventStatusStream.listen((status) {
print('Progress: ${status.progressPercentage}%');
print('Status: ${status.status}');
print('Completed: ${status.completed}/${status.total}');
});
// Start processing
controller.run();
Flutter Example
import 'package:async_event_loader/async_event_loader.dart';
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late AsyncEventLoaderController _controller;
EventStatus? _status;
@override
void initState() {
super.initState();
_initializeController();
}
void _initializeController() {
final events = [
AsyncEvent(
label: 'Initialize',
action: () async => await initializeSystem(),
onSuccess: () => print('Initialized'),
),
AsyncEvent(
label: 'Load Data',
action: () async => await loadData(),
onSuccess: () => print('Data loaded'),
),
];
_controller = AsyncEventLoaderController(
events: events,
retryLimit: 3,
);
_controller.eventStatusStream.listen((status) {
setState(() {
_status = status;
});
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
if (_status != null) ...[
Text('Progress: ${_status!.progressPercentage.toStringAsFixed(1)}%'),
LinearProgressIndicator(
value: _status!.progress / 100,
),
Text('Status: ${_status!.status.name}'),
],
ElevatedButton(
onPressed: () => _controller.run(),
child: Text('Start'),
),
ElevatedButton(
onPressed: () => _controller.pause(),
child: Text('Pause'),
),
ElevatedButton(
onPressed: () => _controller.resume(),
child: Text('Resume'),
),
],
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Advanced Usage 📚
With Timeouts
final controller = AsyncEventLoaderController(
events: [
AsyncEvent(
label: 'Fetch Data',
action: () async => await fetchData(),
timeoutDuration: Duration(seconds: 5), // Event-level timeout
),
],
timeoutDuration: Duration(seconds: 30), // Controller-level timeout
retryLimit: 3,
);
With Error Handling
final controller = AsyncEventLoaderController(
events: [
AsyncEvent(
label: 'Risky Operation',
action: () async {
if (Random().nextBool()) {
throw Exception('Random error');
}
return 'Success';
},
onError: (error) => print('Operation failed: $error'),
),
],
retryLimit: 3,
skipOnError: true, // Continue to next event on error
);
Pause and Resume
// Pause processing
controller.pause();
// Resume processing
controller.resume();
// Reset to initial state
controller.reset();
Status Monitoring
controller.eventStatusStream.listen((status) {
switch (status.status) {
case AsyncStatus.initial:
print('Ready to start');
break;
case AsyncStatus.processing:
print('Processing: ${status.current.label}');
break;
case AsyncStatus.retrying:
print('Retrying (attempt ${status.retryCount})');
break;
case AsyncStatus.completed:
print('All events completed!');
break;
case AsyncStatus.error:
print('Error occurred');
break;
case AsyncStatus.paused:
print('Processing paused');
break;
default:
break;
}
});
API Reference 📖
AsyncEventLoaderController
The main controller for managing async events.
Constructor:
AsyncEventLoaderController({
required List<AsyncEvent> events,
int retryLimit = 3,
bool skipOnError = false,
Duration? timeoutDuration,
})
Methods:
run()- Starts processing eventspause()- Pauses processingresume()- Resumes processing after pausereset()- Resets controller to initial statedispose()- Disposes the controller
Properties:
eventStatusStream- Stream of status updatescurrentEventStatus- Current statusisCompleted- Whether all events are completedisProcessing- Whether currently processingisPaused- Whether pausedisError- Whether an error occurred
AsyncEvent
Represents a single async event to be processed.
Constructor:
AsyncEvent({
required Future<dynamic> Function() action,
void Function()? onSuccess,
void Function()? onRetry,
void Function(dynamic error)? onError,
void Function()? onCancel,
int? order,
String? label,
Duration? timeoutDuration,
})
EventStatus
Represents the current status of event processing.
Properties:
status- CurrentAsyncStatuscurrent- CurrentAsyncEventbeing processedtotal- Total number of eventscompleted- Number of completed eventsretryCount- Current retry countprogress- Progress as a value between 0.0 and 1.0progressPercentage- Progress as a percentage (0-100)
AsyncStatus
Enum representing the various states:
initial- Initial state before processingprocessing- Currently processing an eventretrying- Retrying a failed eventpaused- Processing is pausedcompleted- All events completed successfullyerror- An error occurredcanceled- Processing was canceled
Continuous Integration 🤖
Async Event Loader comes with a built-in GitHub Actions workflow powered by Very Good Workflows but you can also add your preferred CI/CD solution.
Out of the box, on each pull request and push, the CI formats, lints, and tests the code. This
ensures the code remains consistent and behaves correctly as you add functionality or make changes.
The project uses Very Good Analysis for a strict set of analysis options
used by our team. Code coverage is enforced using
the Very Good Workflows.
Libraries
- async_event_loader
- A Dart package for managing and processing asynchronous events sequentially.