Objetos Asíncronos
Descripción General
GetIt proporciona soporte comprehensivo para creación e inicialización asíncrona de objetos. Esto es esencial para objetos que necesitan realizar operaciones async durante la creación (conexiones a base de datos, llamadas de red, I/O de archivos) o que dependen de que otros objetos async estén listos primero.
Capacidades clave:
- ✅ Async Factories - Crea nuevas instancias asíncronamente en cada acceso
- ✅ Async Singletons - Crea singletons con inicialización asíncrona
- ✅ Gestión de Dependencias - Espera automáticamente a dependencias antes de la inicialización
- ✅ Orquestación de Inicio - Coordina secuencias complejas de inicialización
- ✅ Señalización Manual - Control detallado sobre el estado de listo
Referencia Rápida
Métodos de Registro Asíncrono
| Método | Cuándo se Crea | Cuántas Instancias | Tiempo de Vida | Mejor Para |
|---|---|---|---|---|
| registerFactoryAsync | Cada getAsync() | Muchas | Por solicitud | Operaciones async en cada acceso |
| registerCachedFactoryAsync | Primer acceso + después del GC | Reutilizado mientras está en memoria | Hasta que es recolectado por el GC | Optimización de rendimiento para operaciones async costosas |
| registerSingletonAsync | Inmediatamente en el registro | Una | Permanente | Servicios a nivel de app con configuración async |
| registerLazySingletonAsync | Primer getAsync() | Una | Permanente | Servicios async costosos no siempre necesarios |
| registerSingletonWithDependencies | Después de que las dependencias estén listas | Una | Permanente | Servicios que dependen de otros servicios |
Async Factories
Las async factories crean una nueva instancia en cada llamada a getAsync() ejecutando una función factory asíncrona.
registerFactoryAsync
Crea una nueva instancia cada vez que llamas getAsync<T>().
void registerFactoryAsync<T extends Object>(
FactoryFuncAsync<T> factoryFunc, {
String? instanceName,
});Parámetros:
factoryFunc- Función async que crea y devuelve la instanciainstanceName- Nombre opcional para registrar múltiples factories del mismo tipo
Ejemplo:
void main() async {
// Register async factory
getIt.registerFactoryAsync<DatabaseConnection>(
() async {
final conn = DatabaseConnection();
await conn.connect();
return conn;
},
);
// Usage - creates new instance each time
final db1 = await getIt.getAsync<DatabaseConnection>();
final db2 = await getIt.getAsync<DatabaseConnection>(); // Different instance
print('db1 == db2: ${identical(db1, db2)}'); // false - different instances
}registerCachedFactoryAsync
Como registerFactoryAsync, pero cachea la instancia con una referencia débil. Devuelve la instancia cacheada si todavía está en memoria; de lo contrario crea una nueva.
void registerCachedFactoryAsync<T extends Object>(
FactoryFuncAsync<T> factoryFunc, {
String? instanceName,
});Ejemplo:
void main() async {
// Cached async factory
getIt.registerCachedFactoryAsync<HeavyResource>(
() async {
final resource = HeavyResource();
await resource.initialize();
return resource;
},
);
// First access - creates new instance
final resource1 = await getIt.getAsync<HeavyResource>();
// While still in memory - returns cached instance
final resource2 = await getIt.getAsync<HeavyResource>();
print(
'resource1 == resource2: ${identical(resource1, resource2)}'); // true - same instance
}Async Factories con Parámetros
Como las factories regulares, las async factories pueden aceptar hasta dos parámetros.
void registerFactoryParamAsync<T, P1, P2>(
FactoryFuncParamAsync<T, P1, P2> factoryFunc, {
String? instanceName,
});
void registerCachedFactoryParamAsync<T, P1, P2>(
FactoryFuncParamAsync<T, P1, P2> factoryFunc, {
String? instanceName,
});Ejemplo:
// Register async factory with two parameters
getIt.registerFactoryParamAsync<UserViewModel, String, int>(
(userId, age) async {
// Simulate async initialization (e.g., fetch from API)
await Future.delayed(Duration(milliseconds: 100));
return UserViewModel(userId, age: age);
},
);
// Access with parameters
final vm = await getIt.getAsync<UserViewModel>(
param1: 'user-123',
param2: 25,
);Async Singletons
Los async singletons se crean una vez con inicialización asíncrona y viven durante el tiempo de vida del registro (hasta que se desregistren o se haga pop del scope).
registerSingletonAsync
Registra un singleton con una función factory async que se ejecuta inmediatamente. El singleton se marca como listo cuando la función factory se completa (a menos que signalsReady sea true).
void registerSingletonAsync<T extends Object>(
FactoryFuncAsync<T> factoryFunc, {
String? instanceName,
Iterable<Type>? dependsOn,
bool? signalsReady,
DisposingFunc<T>? dispose,
void Function(T instance)? onCreated,
});Parámetros:
factoryFunc- Función async que crea la instancia singletoninstanceName- Nombre opcional para registrar múltiples singletons del mismo tipodependsOn- Lista de tipos de los que depende este singleton (espera a que estén listos primero)signalsReady- Si es true, debes llamar manualmente asignalReady()para marcar como listodispose- Función de limpieza opcional llamada al desregistrar o resetearonCreated- Callback opcional invocado después de que la instancia es creada
Ejemplo:
class RestService {
Future<RestService> init() async {
// do your async initialisation...
await Future.delayed(Duration(seconds: 2));
return this;
}
}
Future<void> setup() async {
// Pattern: Create instance and call init() in one expression
getIt.registerSingletonAsync<RestService>(() async => RestService().init());
// Wait for all async singletons to be ready
await getIt.allReady();
// Now access normally
final service = getIt<RestService>();
}Error Común: Usando signalsReady con registerSingletonAsync
La mayoría del tiempo, NO necesitas signalsReady: true con registerSingletonAsync.
La completación de la factory async automáticamente señaliza ready cuando retorna. Solo usa signalsReady: true si necesitas inicialización multi-etapa donde la factory se completa pero tienes trabajo async adicional antes de que la instancia esté verdaderamente lista.
Patrón de error común:
class MyService {
Future<void> initialize() async {
await Future.delayed(Duration(milliseconds: 100));
}
}
void configureIncorrect() {
// ❌ This will throw: "This instance is not available in GetIt"
getIt.registerSingletonAsync<MyService>(
() async {
final service = MyService();
await service.initialize();
// ❌ ERROR: Instance not in GetIt yet!
// The factory hasn't returned, so GetIt doesn't know about this instance
getIt.signalReady(service);
return service;
},
signalsReady: true,
);
}Por qué falla: No puedes llamar signalReady(instance) desde dentro de la factory porque la instancia aún no está registrada.
Alternativas correctas:
Opción 1 - Dejar que la async factory auto-señalice (recomendado):
void configure() {
// ✅ Correct - no signalsReady needed
getIt.registerSingletonAsync<MyService>(
() async {
final service = MyService();
await service.initialize();
return service; // Automatically signals ready
},
);
}Opción 2 - Usar registerSingleton para señalización post-registro:
class MyService {
MyService() {
_initialize();
}
Future<void> _initialize() async {
await loadData();
GetIt.instance.signalReady(this); // ✅ Now it's in GetIt
}
Future<void> loadData() async {
await Future.delayed(Duration(milliseconds: 100));
}
}
void configure() {
// ✅ Correct - instance registered immediately
getIt.registerSingleton<MyService>(
MyService(),
signalsReady: true, // Must manually signal
);
}Opción 3 - Implementar la interfaz WillSignalReady:
class MyService implements WillSignalReady {
MyService() {
_initialize();
}
Future<void> _initialize() async {
await loadData();
GetIt.instance.signalReady(this);
}
Future<void> loadData() async {
await Future.delayed(Duration(milliseconds: 100));
}
}
void configure() {
// ✅ Correct - interface-based signaling
getIt.registerSingleton<MyService>(MyService());
// No signalsReady parameter needed - interface detected
}Mira Señalización Manual de Ready para más detalles.
registerLazySingletonAsync
Registra un singleton con una función factory async que se ejecuta en el primer acceso (cuando llamas getAsync<T>() por primera vez).
void registerLazySingletonAsync<T extends Object>(
FactoryFuncAsync<T> factoryFunc, {
String? instanceName,
DisposingFunc<T>? dispose,
void Function(T instance)? onCreated,
bool useWeakReference = false,
});Parámetros:
factoryFunc- Función async que crea la instancia singletoninstanceName- Nombre opcional para registrar múltiples singletons del mismo tipodispose- Función de limpieza opcional llamada al desregistrar o resetearonCreated- Callback opcional invocado después de que la instancia es creadauseWeakReference- Si es true, usa referencia débil (permite recolección de basura si no se usa)
Ejemplo:
void configureDependencies() {
// Lazy async singleton - created on first access
getIt.registerLazySingletonAsync<CacheService>(
() async {
final cache = CacheService();
await cache.loadFromDisk();
return cache;
},
);
// With weak reference - allows GC when not in use
getIt.registerLazySingletonAsync<ImageCache>(
() async => ImageCache.load(),
useWeakReference: true,
);
}
Future<void> main() async {
configureDependencies();
// First access - triggers creation
final cache = await getIt.getAsync<CacheService>();
// Subsequent access - returns existing instance
final cache2 = await getIt.getAsync<CacheService>(); // Same instance
}Lazy Async Singletons y allReady()
registerLazySingletonAsync no bloquea allReady() porque la función factory no se llama hasta el primer acceso. Sin embargo, una vez accedido, puedes usar isReady() para esperar a su completación.
Accediendo a Objetos Async
Los Objetos Async se Vuelven Normales Después de la Inicialización
Una vez que un async singleton ha completado la inicialización (has hecho await de allReady() o isReady<T>()), puedes accederlo como un singleton regular usando get<T>() en lugar de getAsync<T>(). Los métodos async solo se necesitan durante la fase de inicialización o cuando accedes a async factories.
// Durante el inicio - espera la inicialización
await getIt.allReady();
// Después de estar listo - accede normalmente (no se necesita await)
final database = getIt<Database>(); // ¡No getAsync!
final apiClient = getIt<ApiClient>();getAsync()
Recupera una instancia creada por una async factory o espera a que un async singleton complete la inicialización.
Future<T> getAsync<T>({
String? instanceName,
dynamic param1,
dynamic param2,
Type? type,
})Ejemplo:
// Get async factory instance
final conn = await getIt.getAsync<DatabaseConnection>();
// Get async singleton (waits if still initializing)
final api = await getIt.getAsync<ApiClient>();
// Get named instance
final cache = await getIt.getAsync<CacheService>(instanceName: 'user-cache');
// Get with parameters (async factory param)
final report = await getIt.getAsync<Report>(
param1: 'user-123',
param2: DateTime.now(),
);Obteniendo Múltiples Instancias Async
Si necesitas recuperar múltiples registros async del mismo tipo, mira el capítulo Registros Múltiples para la documentación de getAllAsync().
Gestión de Dependencias
Usando dependsOn
El parámetro dependsOn asegura el orden de inicialización. Cuando registras un singleton con dependsOn, su función factory no se ejecutará hasta que todas las dependencias listadas hayan señalizado ready.
Ejemplo - Inicialización secuencial:
void configureDependencies() {
// 1. Config loads first (no dependencies)
getIt.registerSingletonAsync<ConfigService>(
() async {
final config = ConfigService();
await config.loadFromFile();
return config;
},
);
// 2. API client waits for config
getIt.registerSingletonAsync<ApiClient>(
() async {
final apiUrl = getIt<ConfigService>().apiUrl;
final client = ApiClient(apiUrl);
await client.authenticate();
return client;
},
dependsOn: [ConfigService],
);
// 3. Database waits for config
getIt.registerSingletonAsync<Database>(
() async {
final dbPath = getIt<ConfigService>().databasePath;
final db = Database(dbPath);
await db.initialize();
return db;
},
dependsOn: [ConfigService],
);
// 4. App model waits for everything
getIt.registerSingletonWithDependencies<AppModel>(
() => AppModel.withParams(
api: getIt<ApiClient>(),
db: getIt<Database>(),
config: getIt<ConfigService>()),
dependsOn: [ConfigService, ApiClient, Database],
);
}Singletons Sincrónicos con Dependencias
A veces tienes un singleton regular (sync) que depende de que otros async singletons estén listos primero. Usa registerSingletonWithDependencies para este patrón.
void registerSingletonWithDependencies<T>(
FactoryFunc<T> factoryFunc, {
String? instanceName,
required Iterable<Type>? dependsOn,
bool? signalsReady,
DisposingFunc<T>? dispose,
})Parámetros:
factoryFunc- Función sync que crea la instancia singleton (llamada después de que las dependencias estén listas)instanceName- Nombre opcional para registrar múltiples singletons del mismo tipodependsOn- Lista de tipos de los que depende este singleton (espera a que estén listos primero)signalsReady- Si es true, debes llamar manualmente asignalReady()para marcar como listodispose- Función de limpieza opcional llamada al desregistrar o resetear
Ejemplo:
void configureDependencies() {
// Async singletons
getIt.registerSingletonAsync<ConfigService>(
() async => ConfigService.load(),
);
getIt.registerSingletonAsync<ApiClient>(
() async => ApiClient.create(),
);
// Sync singleton that depends on async singletons
getIt.registerSingletonWithDependencies<UserRepository>(
() => UserRepository(getIt<ApiClient>(), getIt<Database>()),
dependsOn: [ConfigService, ApiClient],
);
}
Future<void> main() async {
configureDependencies();
// Wait for all to be ready
await getIt.allReady();
// Now safe to access - dependencies are guaranteed ready
final userRepo = getIt<UserRepository>();
}Orquestación de Inicio
GetIt proporciona varias funciones para coordinar la inicialización async y esperar a que los servicios estén listos.
allReady()
Devuelve un Future<void> que se completa cuando todos los async singletons y singletons con signalsReady han completado su inicialización.
Future<void> allReady({
Duration? timeout,
bool ignorePendingAsyncCreation = false,
})Parámetros:
timeout- Timeout opcional; lanzaWaitingTimeOutExceptionsi no está listo a tiempoignorePendingAsyncCreation- Si es true, solo espera señales manuales, ignora async singletons
Ejemplo con FutureBuilder:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getIt.allReady(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// All services ready - show main app
return HomePage();
} else {
// Still initializing - show splash screen
return SplashScreen();
}
},
);
}
}Ejemplo con timeout:
Future<void> main() async {
setupDependencies();
try {
await getIt.allReady(timeout: Duration(seconds: 10));
runApp(MyApp());
} on WaitingTimeOutException catch (e) {
print('Initialization timeout!');
print('Not ready: ${e.notReadyYet}');
print('Already ready: ${e.areReady}');
print('Waiting chain: ${e.areWaitedBy}');
}
}Llamando a allReady() múltiples veces:
Puedes llamar allReady() múltiples veces. Después de que el primer allReady() se complete, si registras nuevos async singletons, puedes hacer await de allReady() otra vez para esperar a los nuevos.
// Register first batch of services
getIt.registerSingletonAsync<ConfigService>(() async => ConfigService.load());
getIt.registerSingletonAsync<Logger>(() async => Logger.initialize());
// Wait for first batch
await getIt.allReady();
print('Core services ready');
// Register second batch based on config
final config = getIt<ConfigService>();
if (config.enableFeatureX) {
getIt.registerSingletonAsync<FeatureX>(() async => FeatureX.initialize());
}
// Wait for second batch
await getIt.allReady();
print('All services ready');
runApp(MyApp());Este patrón es especialmente útil con scopes donde cada scope necesita su propia inicialización:
Future<void> main() async {
// Initialize base scope
await getIt.allReady();
// Push new scope with its own async services
getIt.pushNewScope(scopeName: 'user-session');
getIt.registerSingletonAsync<UserService>(() async => UserService.load());
// Wait for new scope to be ready
await getIt.allReady();
}isReady()
Devuelve un Future<void> que se completa cuando un singleton específico está listo.
Future<void> isReady<T>({
Object? instance,
String? instanceName,
Duration? timeout,
Object? callee,
})Parámetros:
T- Tipo del singleton a esperarinstance- Alternativamente, esperar a un objeto de instancia específicoinstanceName- Esperar a registro con nombretimeout- Timeout opcional; lanzaWaitingTimeOutExceptionsi no está listo a tiempocallee- Parámetro opcional para depuración (ayuda a identificar quién está esperando)
Ejemplo:
Future<void> main() async {
// setupDependencies(); // Setup your dependencies first
// Wait for specific service
await getIt.isReady<Database>();
// Now safe to use
final db = getIt<Database>();
// Wait for named instance
await getIt.isReady<ApiClient>(instanceName: 'production');
}isReadySync()
Verifica si un singleton está listo sin esperar (devuelve inmediatamente).
bool isReadySync<T>({
Object? instance,
String? instanceName,
})Ejemplo:
void checkStatus() {
if (getIt.isReadySync<Database>()) {
print('Database is ready');
} else {
print('Database still initializing...');
}
}allReadySync()
Verifica si todos los async singletons están listos sin esperar.
bool allReadySync([bool ignorePendingAsyncCreation = false])Ejemplo:
void showUI() {
if (getIt.allReadySync()) {
// Show main UI
} else {
// Show loading indicator
}
}Dependencias con Nombre usando InitDependency
Si tienes registros con nombre, usa InitDependency para especificar tanto el tipo como el nombre de instancia.
void configureDependencies() {
// Register multiple API clients
getIt.registerSingletonAsync<ApiClient>(
() async => ApiClient.create('https://api-v1.example.com'),
instanceName: 'api-v1',
);
getIt.registerSingletonAsync<ApiClient>(
() async => ApiClient.create('https://api-v2.example.com'),
instanceName: 'api-v2',
);
// Depend on specific named instance
getIt.registerSingletonWithDependencies<DataSync>(
() => DataSync(getIt<ApiClient>(instanceName: 'api-v2')),
dependsOn: [InitDependency(ApiClient, instanceName: 'api-v2')],
);
}Señalización Manual de Ready
A veces necesitas más control sobre cuándo un singleton señaliza que está listo. Esto es útil cuando la inicialización involucra múltiples pasos o callbacks.
Usando el Parámetro signalsReady
Cuando estableces signalsReady: true durante el registro, GetIt no marcará automáticamente el singleton como listo. Debes llamar manualmente a signalReady().
Ejemplo:
class ConfigService {
bool isReady = false;
ConfigService() {
_initialize();
}
Future<void> _initialize() async {
// Complex async initialization
await loadRemoteConfig();
await validateConfig();
await setupConnections();
isReady = true;
// Signal that we're ready
GetIt.instance.signalReady(this);
}
Future<void> loadRemoteConfig() async {}
Future<void> validateConfig() async {}
Future<void> setupConnections() async {}
}
void configureDependencies() {
getIt.registerSingleton<ConfigService>(
ConfigService(),
signalsReady: true, // Must manually signal ready
);
}
Future<void> main() async {
configureDependencies();
// Wait for ready signal
await getIt.isReady<ConfigService>();
}Usando la Interfaz WillSignalReady
En lugar de pasar signalsReady: true, implementa la interfaz WillSignalReady. GetIt detecta esto automáticamente y espera la señalización manual.
class ConfigService implements WillSignalReady {
bool isReady = false;
ConfigService() {
_initialize();
}
Future<void> _initialize() async {
await loadConfig();
isReady = true;
GetIt.instance.signalReady(this);
}
Future<void> loadConfig() async {}
}
void configureDependencies() {
// No signalsReady parameter needed - interface handles it
getIt.registerSingleton<ConfigService>(ConfigService());
}signalReady()
Señaliza manualmente que un singleton está listo.
void signalReady(Object? instance)Parámetros:
instance- La instancia que está lista (pasarnulles legacy y no se recomienda)
Ejemplo:
class DatabaseService {
DatabaseService() {
_init();
}
Future<void> _init() async {
await connectToDatabase();
await runMigrations();
// Signal this instance is ready
GetIt.instance.signalReady(this);
}
Future<void> connectToDatabase() async {}
Future<void> runMigrations() async {}
}Característica Legacy
signalReady(null) (señal ready global sin una instancia) es una característica legacy de versiones anteriores de GetIt. Se recomienda usar registros async (registerSingletonAsync, etc.) o señalización específica de instancia en su lugar. El enfoque de señal global es menos claro sobre qué se está inicializando y no se integra bien con la gestión de dependencias.
Nota: El signalReady(null) global lanzará un error si tienes cualquier registro async o instancias con signalsReady: true que aún no hayan señalizado. La señalización específica de instancia funciona bien junto con registros async.
Mejores Prácticas
1. Prefiere registerSingletonAsync para Inicialización de App
Para servicios necesarios al inicio de la app, usa registerSingletonAsync (no lazy) para que comiencen a inicializarse inmediatamente.
void configureDependencies() {
// Good - starts initializing immediately
getIt.registerSingletonAsync<Database>(() async => Database.connect());
// Less ideal - won't initialize until first access
getIt.registerLazySingletonAsync<Database>(() async => Database.connect());
}2. Usa dependsOn para Expresar Dependencias
Deja que GetIt gestione el orden de inicialización en lugar de orquestar manualmente con isReady().
// Good - clear dependency chain
void configureDependencies() {
getIt.registerSingletonAsync<ConfigService>(() async => ConfigService.load());
getIt.registerSingletonAsync<ApiClient>(
() async => ApiClient(getIt<ConfigService>().apiUrl),
dependsOn: [ConfigService],
);
}
// Less ideal - manual orchestration
void configureDependenciesManual() {
getIt.registerSingletonAsync<ConfigService>(() async => ConfigService.load());
getIt.registerSingletonAsync<ApiClient>(() async {
await getIt.isReady<ConfigService>(); // Manual waiting
return ApiClient(getIt<ConfigService>().apiUrl);
});
}3. Usa FutureBuilder para Pantallas de Splash
Muestra una pantalla de carga mientras los servicios se inicializan.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: FutureBuilder(
future: getIt.allReady(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return ErrorScreen(snapshot.error!);
}
if (snapshot.hasData) {
return HomePage();
}
return SplashScreen();
},
),
);
}
}4. Siempre Establece Timeouts para allReady()
Previene que tu app se cuelgue indefinidamente si la inicialización falla.
Future<void> main() async {
try {
await getIt.allReady(timeout: Duration(seconds: 30));
runApp(MyApp());
} on WaitingTimeOutException catch (e) {
// Handle timeout - log error, show error screen, etc.
runApp(ErrorApp(error: e));
}
}Patrones Comunes
Patrón 1: Inicialización en Capas
void configureDependencies() {
// Layer 1: Core infrastructure
getIt.registerSingletonAsync<ConfigService>(() async => ConfigService.load());
getIt.registerSingletonAsync<Logger>(() async => Logger.initialize());
// Layer 2: Network and data access
getIt.registerSingletonAsync<ApiClient>(
() async => ApiClient(getIt<ConfigService>().apiUrl),
dependsOn: [ConfigService],
);
getIt.registerSingletonAsync<Database>(
() async => Database(getIt<ConfigService>().dbPath),
dependsOn: [ConfigService],
);
// Layer 3: Business logic
getIt.registerSingletonWithDependencies<UserRepository>(
() => UserRepository(getIt<ApiClient>(), getIt<Database>()),
dependsOn: [ApiClient, Database],
);
// Layer 4: Application state
getIt.registerSingletonWithDependencies<AppModel>(
() => AppModel(getIt<UserRepository>()),
dependsOn: [UserRepository],
);
}Patrón 2: Inicialización Condicional
void configureDependencies({required bool isProduction}) {
getIt.registerSingletonAsync<ConfigService>(
() async => ConfigService.load(),
);
if (isProduction) {
getIt.registerSingletonAsync<ApiClient>(
() async => ApiClient(getIt<ConfigService>().prodUrl),
dependsOn: [ConfigService],
);
} else {
getIt.registerSingletonAsync<ApiClient>(
() async => MockApiClient(),
dependsOn: [ConfigService],
);
}
}Patrón 3: Seguimiento de Progreso
class InitializationProgress extends ChangeNotifier {
final Map<String, bool> _progress = {};
void markReady(String serviceName) {
_progress[serviceName] = true;
notifyListeners();
}
double get percentComplete =>
_progress.values.where((ready) => ready).length / _progress.length;
}
void configureDependencies(InitializationProgress progress) {
getIt.registerSingletonAsync<ConfigService>(
() async => ConfigService.load(),
onCreated: (_) => progress.markReady('Config'),
);
getIt.registerSingletonAsync<Database>(
() async => Database.connect(),
dependsOn: [ConfigService],
onCreated: (_) => progress.markReady('Database'),
);
getIt.registerSingletonAsync<ApiClient>(
() async => ApiClient.create(),
dependsOn: [ConfigService],
onCreated: (_) => progress.markReady('API'),
);
}Patrón 4: Reintentar en Fallo
Future<T> withRetry<T>(
Future<T> Function() operation, {
int maxAttempts = 3,
}) async {
for (var attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await operation();
} catch (e) {
if (attempt == maxAttempts) rethrow;
await Future.delayed(Duration(seconds: attempt));
}
}
throw StateError('Should never reach here');
}
void configureDependencies() {
getIt.registerSingletonAsync<ApiClient>(
() => withRetry(() async => ApiClient.connect()),
);
}Lectura Adicional
- Post de blog detallado sobre async factories y orquestación de inicio
- Documentación de Scopes - Inicialización async dentro de scopes
- Documentación de Pruebas - Simulando servicios async en pruebas