Menu
Coddy logo textTech

Constructores asíncronos

Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 79 de 110.

Los constructores en Dart no pueden marcarse como async; deben devolver una instancia de la clase inmediatamente, no un Future. ¿Entonces, cómo se crean objetos que necesitan una inicialización asíncrona, como cargar datos de una base de datos o recuperar una configuración?

La solución es utilizar un método de fábrica estático asíncrono. Mantén el constructor privado (o simple) y proporciona un método estático que realice el trabajo asíncrono y devuelva un Future de tu clase:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // Constructor privado
  
  static Future<UserProfile> create(String userId) async {
    // Simular la obtención de datos
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

El constructor privado UserProfile._ maneja la creación real del objeto, mientras que el método estático create() maneja todo el trabajo asíncrono. Los llamadores usan await para obtener el objeto completamente inicializado.

Este patrón asegura que su objeto esté completamente listo para usar una vez que lo tenga: sin estados parcialmente inicializados ni datos faltantes. Se utiliza comúnmente para clases que necesitan cargar recursos, conectarse a servicios o realizar cualquier configuración que lleve tiempo.

challenge icon

Desafío

Fácil

¡Vamos a construir un cargador de configuración que demuestre cómo manejar la inicialización asíncrona en las clases! Dado que los constructores no pueden ser asíncronos, utilizarás el patrón de método de fábrica estático para crear objetos que necesitan tiempo para "cargar" sus datos.

Organizarás tu código en dos archivos:

  • config_loader.dart: Crea una clase ConfigLoader que simule la carga de ajustes de configuración de forma asíncrona. Tu clase debe tener:
    • Tres campos finales: appName (String), version (String) y maxUsers (int)
    • Un constructor privado que inicialice los tres campos
    • Un método de fábrica estático y asíncrono llamado load que tome un parámetro String configId y devuelva un Future<ConfigLoader>. Este método debe:
      • Usar Future.delayed() con 100 milisegundos para simular el tiempo de carga
      • Generar los valores de configuración basados en el configId:
        • appName: el configId con " App" concatenado
        • version: "1.0." seguido de la longitud de configId
        • maxUsers: la longitud de configId multiplicada por 10
      • Devolver una nueva instancia de ConfigLoader con estos valores
    • Un método displayInfo() que imprima la configuración en este formato: Config: [appName] v[version] (max [maxUsers] users)
  • main.dart: Importa tu cargador de configuración y demuestra el patrón de inicialización asíncrona:
    • Imprime Loading configurations...
    • Carga una configuración con configId "Production" y llama a su displayInfo()
    • Carga una configuración con configId "Dev" y llama a su displayInfo()
    • Imprime All configs loaded!

Observa cómo el método estático load maneja todo el trabajo asíncrono, mientras que el constructor privado simplemente asigna los valores. ¡Los llamadores usan await para obtener un objeto completamente inicializado!

Salida esperada:

Loading configurations...
Config: Production App v1.0.10 (max 100 users)
Config: Dev App v1.0.3 (max 30 users)
All configs loaded!

Hoja de referencia

Los constructores en Dart no pueden marcarse como async porque deben devolver una instancia de la clase inmediatamente, no un Future.

Para crear objetos que necesiten una inicialización asíncrona, utiliza un método de fábrica estático asíncrono:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // Constructor privado
  
  static Future<UserProfile> create(String userId) async {
    // Realiza el trabajo asíncrono aquí
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

Uso:

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

El constructor privado (marcado con _) se encarga de la creación del objeto, mientras que el método estático se encarga del trabajo asíncrono. Esto garantiza que los objetos estén completamente inicializados antes de su uso.

Pruébalo tú mismo

import 'config_loader.dart';

void main() async {
  // TODO: Imprimir "Loading configurations..."
  
  // TODO: Cargar una configuración con configId "Production"
  // y llamar a su método displayInfo()
  
  // TODO: Cargar una configuración con configId "Dev"
  // y llamar a su método displayInfo()
  
  // TODO: Imprimir "All configs loaded!"
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos