spotAsync<T> static method
Resolves and returns an async singleton of type T.
Use this for services registered with registerAsync that require asynchronous initialization. The instance is created once and cached.
Can also be used to resolve regular singletons asynchronously.
Type Parameters:
T: The type to resolve (must be registered)
Parameters:
name: Optional name qualifier for named instances
Returns: Future<T> that resolves to the instance
Throws:
- SpotException if
Tis not registered - SpotException if circular dependency detected
Example:
// Resolve async singleton
final db = await Spot.spotAsync<Database>();
final api = await spotAsync<IApiClient>();
// Named instance
final remoteCache = await spotAsync<Cache>(name: 'remote');
// In async context
Future<void> initApp() async {
final db = await spotAsync<Database>();
await db.loadInitialData();
}
// Multiple async dependencies
final results = await Future.wait([
spotAsync<Database>(),
spotAsync<IApiClient>(),
spotAsync<IAuthService>(),
]);
See also:
- registerAsync for async singleton registration
- spot for synchronous resolution
Implementation
static Future<T> spotAsync<T>({String? name}) async {
final key = SpotKey<T>(T, name);
// Fast path: check singleton cache first for performance
if (_singletonCache.containsKey(key)) {
if (logging) log.v('Cache hit for async $key');
return _singletonCache[key] as T;
}
if (!registry.containsKey(key)) {
final registeredTypes = registry.keys.map((k) => k.toString()).join(', ');
throw SpotException(
'Type $key is not registered in Spot container.\n'
'Registered types: ${registeredTypes.isNotEmpty ? registeredTypes : '(none)'}\n\n'
'Did you forget to register it in SpotModule or with Spot.registerAsync()?'
);
}
// Check for circular dependency
if (_resolutionStack.contains(key)) {
final cycle = [..._resolutionStack, key].map((k) => k.toString()).join(' -> ');
throw SpotException(
'Circular dependency detected: $cycle\n'
'Cannot resolve $key because it depends on itself (directly or indirectly).'
);
}
_resolutionStack.add(key);
try {
if (logging) log.v('Async injecting $key -> ${registry[key]!.targetType}');
final service = registry[key]!;
final instance = await service.locateAsync();
if (instance == null) {
throw SpotException('Class $key resolved to null');
}
// Cache initialized async singletons for faster subsequent access
if (service.type == SpotType.asyncSingleton && service.instance != null) {
_singletonCache[key] = instance;
if (logging) log.v('Cached async singleton $key');
}
return instance;
} catch (e) {
if (e is SpotException) {
rethrow; // Re-throw SpotException as-is
}
log.e('Failed to async locate class $key', e);
throw SpotException('Failed to async resolve $key: ${e.toString()}');
} finally {
_resolutionStack.removeLast();
}
}