isolator 0.0.4 isolator: ^0.0.4 copied to clipboard
Isolator is a package, which offer to you a simple way for creating two-component states with isolated part and frontend part of any kind - BLoC, MobX, ChangeNotifier and others
isolator #
Isolator is a package, which offer to you a simple way for creating two-component states with isolated part and frontend part of any kind (BLoC, MobX, ChangeNotifier and many others).
This package is a trying to proof of concept, when you take out heavy business logic to isolates for achievement a fully cleared from any lugs application. With this package you can easy create a so-called "backend" - class with your logic and second class, which uses a special mixin - a state of any kind - BLoC / MobX / ChangeNotifier (as in an example).
Example #
Main isolate class, who are a consumer of Backend - it has a name Frontend
/// Event id - you can use any of you want
enum FirstEvents {
increment,
decrement,
createError,
}
class FirstState with Frontend<FirstEvents>, ChangeNotifier {
FirstState(this._someOtherService);
SomeOtherService _someOtherService;
int counter = 209;
void increment([int diff = 1]) {
send(FirstEvents.increment, diff);
}
/// You can run any backend method in synchronous mode with method [runBackendMethod] of Frontend
Future<void> decrement([int diff = 1]) async {
counter = await runBackendMethod(FirstEvents.decrement, diff);
}
void _setCounter(int value) {
counter = value;
/// Manual notification
notifyListeners();
}
void _resetCounter(dynamic error) {
counter = 0;
}
void throwError() {
send(FirstEvents.createError);
}
void _syntheticErrorHandler(dynamic error) {
print('The error was thrown in backend layer $error');
}
Future<void> initState() async {
/// [initBackend] method of Frontend used for creating a Backend instance in isolate
/// creating function should write manually, example is below
/// [data] and [errorHandler] is a optional fields for initialization of isolates
await initBackend(createFirstBackend, data: data, errorHandler: errorHandler);
}
/// Hook, which calling after every message from backend of this state
@override
void onBackendResponse() {
notifyListeners();
}
/// Hook on every error
@override
Future<void> onError(dynamic error) async {
print('Error in frontend state $error');
}
/// [tasks] - it is a getter, which return pairs of Event and Function, which called automatically on correspond Message from Backend
@override
Map<FirstEvents, Function> get tasks => {
FirstEvents.increment: _setCounter,
FirstEvents.decrement: _setCounter,
FirstEvents.error: _setCounter,
};
/// [tasks] - it is a getter, which return pairs of Event and Function, which called automatically on correspond Message from Backend
@override
Map<FirstEvents, Function> get errorsHandlers => {
FirstEvents.increment: _setCounter,
};
/// Functions (handlers), which will executed, when error was thrown in backend after sending corresponding message with [eventId] from frontend
/// each handler can have a one dynamic argument - then in that handlers will pass a error
@override
Map<FirstEvents, ErrorHandler> get errorsHandlers => {
FirstEvents.createError: _syntheticErrorHandler,
};
}
Backend - class, which will be placed at outside isolate with main business logic
/// Function, which was written by hand
void createFirstBackend(BackendArgument<void> argument) {
FirstBackend(argument);
}
class FirstBackend extends Backend<FirstEvents, TDataType> {
FirstBackend(BackendArgument<TDataType> argument) : super(argument);
int counter = 209;
/// To send data back to the frontend, you can use manually method [send]
void _increment(int diff) {
counter += diff;
send(FirstEvents.increment, counter);
}
/// Or, you can simply return a value
/// If you have a plans to running methods of Backend in synchronous mode - then, this methods must always return a value
/// but this methods still have possibility to send others messages with events before return a value
int _decrement(int diff) {
counter -= diff;
return counter;
}
void _createError() {
throw Exception('Synthetic exception');
}
/// [operations] - it is a getter, which return pairs of Event and Function, which called automatically on correspond Message from Frontend
@override
Map<FirstEvents, Function> get operations => {
FirstEvents.increment: _increment,
FirstEvents.decrement: _decrement,
FirstEvents.createError: _createError,
};
}
Also, you can configure Isolator with these params:
Future<void> onBackendError(dynamic error) async {
print('Backend error observer was called with error $error');
}
Future<void> onDataLoadFromBackend(Message<dynamic, dynamic> message) async {
print('Got a message from backend in observer $message');
}
Future<void> main() async {
IsolatorConfig.setTransferTimeLogging(true);
IsolatorConfig.setLogging(true);
IsolatorConfig.setValuesShowing(false);
IsolatorConfig.setBackendErrorsObservers([onBackendError]);
IsolatorConfig.setFrontendObservers([onDataLoadFromBackend]);
runApp(MyApp());
}
These params will apply in the main thread and in all isolates.
Restrictions #
- Backend classes can't use a native layer (method-channel)
- For one backend - one isolate (too many isolates take much time for initialization, for example: ~6000ms for 30 isolates at emulator in dev mode)