-
-
Save escamoteur/c4c717518729a4c24a4ce95c10111473 to your computer and use it in GitHub Desktop.
| GetIt supports since recently a mechanic to make initialization of registered | |
| Instances easier. | |
| The idea is that you can mark Singletons/LazySingletons that they will signal | |
| when they are ready. | |
| You then can wait for either that all are ready or that one of them is ready or | |
| trigger the readyFuture manually | |
| In case of a timeout you get detailed debug information. | |
| /// Returns a Future that is completed once all registered Singletons have signaled that they are ready | |
| /// Or when the global [signalReady] is called without an instance | |
| /// [timeOut] if this is set and the future wasn't completed within that time period an | |
| Future<void> allReady({Duration timeOut}); | |
| /// Returns a Future that is completed when a given registered Singletons has signaled that it is ready | |
| /// [T] Type of the Singleton to be waited for | |
| /// [instance] registered instance to be waited for | |
| /// [instanceName] Singleton to be waited for that was registered by name instead of a type. | |
| /// You should only use one of the | |
| /// [timeOut] if this is set and the future wasn't completed within that time period an | |
| /// [callee] optional parameter which makes debugging easier. Pass `this` in here. | |
| Future<void> isReady<T>( | |
| {Object instance, String instanceName, Duration timeOut, Object callee}); | |
| /// if [instance] is `null` and no singleton is waiting to be signaled this will complete the future you got | |
| /// from [allReady] | |
| /// | |
| /// If [instance] has a value GetIt will search for the responsible singleton and complete all futures you might | |
| /// have received by calling [isReady] | |
| /// Typically this is use in this way inside the registered objects init method `GetIt.instance.signalReady(this);` | |
| /// If all waiting singletons/factories have signaled ready the future you can get from [allReady] is automatically completed | |
| /// | |
| /// Both ways are mutual exclusive meaning either only use the global `signalReady()` and don't register a singlton/fatory as signaling ready | |
| /// Or let indiviual instance signal their ready state on their own. | |
| void signalReady([Object instance]); | |
| /// In case of an timeout while waiting for an instance to signal ready | |
| /// This exception is thrown whith information about who is still waiting | |
| class WaitingTimeOutException implements Exception { | |
| /// if you pass the [callee] parameter to [isReady] | |
| /// this maps lists which callees is waiting for whom | |
| final Map<Type, Type> isWaitingFor; | |
| /// Lists with Types that are still not ready | |
| final List<Type> notSignaledYet; | |
| /// Lists with Types that are already ready | |
| final List<Type> hasSignaled; | |
| WaitingTimeOutException( | |
| this.isWaitingFor, this.notSignaledYet, this.hasSignaled) | |
| : assert(isWaitingFor != null && | |
| notSignaledYet != null && | |
| hasSignaled != null); | |
| @override | |
| String toString() { | |
| print( | |
| 'GetIt: There was a timeout while waiting for an instance to signal ready'); | |
| print('The following instance types where waiting for completion'); | |
| for (var entry in isWaitingFor.entries) { | |
| print('${entry.key} is waiting for ${entry.value}'); | |
| } | |
| print('The following instance types have NOT signaled ready yet'); | |
| for (var entry in notSignaledYet) { | |
| print('$entry'); | |
| } | |
| print('The following instance types HAVE signaled ready yet'); | |
| for (var entry in hasSignaled) { | |
| print('$entry'); | |
| } | |
| return super.toString(); | |
| } | |
| } |
I implemented a different mechanism which almost automatically signals that an instance is ready by passing async builder functions to new async registration functions.
See: https://pub.dev/packages/get_it/versions/4.0.0-beta3
But I'm really unsure because when integrating this system in my current app the result looks not as easy to read and is more cumbersome. Compare
old:
backend.registerSingleton<ErrorReporter>(reporter);
backend.registerSingleton<SystemService>(
SystemServiceImplementation(),
);
backend.registerSingleton<ConfigService>(
ConfigServiceImplementation(),
signalsReady: true,
);
backend.registerSingleton<RTSAService>(
RTSAServiceImplementation(),
signalsReady: true,
);
backend.registerSingleton<MapTileService>(
MapTileServiceImplementation(),
signalsReady: false, // check if this is necessary outside of rtsa_map
);
backend.registerLazySingleton<InteractionService>(
() => InteractionServiceImplementation(signalsReady: false),
);
backend.registerLazySingleton<GeoLocationService>(
() => GeoLocationServiceImplementation());
backend.registerSingleton<DetectionManager>(
DetectionManager(),
signalsReady: false,
);
backend.registerSingleton<MapManager>(
MapManager(),
signalsReady: true,
);
///and later:
backend<ConfigService>().loadAppConfig().then((config) {
backend<ErrorReporter>().setServerConfig(config.serverAdress,config.port);
backend<RTSAService>().init(config.serverAdress, config.port);
backend<MapTileService>().init(config.serverAdress, config.port);
backend<GeoLocationService>().init(
useFixedUserPosition: config.useFixedUserPosition ?? false,
fixedUserPosition:
LatLng(config.fixedUserLatitude ?? 0, config.fixedUserLongitude ?? 0));
backend<DetectionManager>().init(appConfig: config);
backend<MapManager>().init(
useCustomMap: config.useCustomMap,
customMapName: config.customMapName,
);
});to the new Version without separate Initialization:
backend.registerSingletonAsync<ConfigService>(
(_) => ConfigServiceImplementation()..loadAppConfig());
backend.registerSingletonAsync<ErrorReporter>((_) async {
var config = backend<ConfigService>().currentConfig;
return reporter
..setServerConfig(
config.serverAdress,
config.port,
);
}, dependsOn: [ConfigService]);
backend.registerSingletonAsync<RTSAService>(
(_) async => RTSAServiceImplementation(),
dependsOn: [ConfigService]);
backend.registerSingletonAsync<MapTileService>((_) async {
var config = backend<ConfigService>().currentConfig;
return MapTileServiceImplementation()
..init(
config.serverAdress,
config.port,
);
}, dependsOn: [ConfigService]);
backend.registerSingletonAsync<InteractionService>(
(completer) => InteractionServiceImplementation(completer),
);
backend.registerSingletonAsync<GeoLocationService>((_) async {
var config = backend<ConfigService>().currentConfig;
return GeoLocationServiceImplementation()
..init(
useFixedUserPosition: config.useFixedUserPosition ?? false,
fixedUserPosition: LatLng(
config.fixedUserLatitude ?? 0, config.fixedUserLongitude ?? 0));
}, dependsOn: [ConfigService]);
backend.registerSingletonAsync<DetectionManager>(
(_) async => DetectionManager(),
dependsOn: [ConfigService]);
backend.registerSingletonAsync<MapManager>((_) async => MapManager(),
dependsOn: [ConfigService]);So maybe back to the drawing board and pick the best of both worlds.
@Kavantix @Lootwig
The idea of having to implement a certain interface is interesting but I'm not sure if we limit the way a registered instance is inherited. Also haveing signalsReady at the same place as the registration makes it easier to see which of them might act asynchronously.
@Kavantix
(note the added "extends" restriction)?