Menu
CoddyTech

异步构造函数

本课属于 Coddy Dart 学习旅程的“面向对象编程”单元。第 79 课,共 110 课。

Dart 中的构造函数不能标记为 async,因为它们必须立即返回类的实例,而不是 Future。那么,如何创建需要异步初始化的对象呢?例如,需要从数据库加载数据或获取配置的对象。

解决方案是使用静态异步工厂方法。将构造函数设为私有(或保持简单),并提供一个执行异步操作并返回类的 Future 的静态方法:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // 私有构造函数
  
  static Future<UserProfile> create(String userId) async {
    // 模拟获取数据
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

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

私有构造函数 UserProfile._ 负责实际创建对象,而静态 create() 方法负责处理所有异步工作。调用方使用 await 获取完全初始化的对象。

这种模式可以确保你拿到对象后,它已经完全可以使用,不会处于未完成初始化或缺少数据的状态。它通常用于需要加载资源、连接服务或执行耗时设置的类。

challenge icon

挑战

简单

我们来构建一个配置加载器,看看如何处理类中的异步初始化!由于构造函数不能是异步的,你将使用静态工厂方法模式来创建需要时间“加载”数据的对象。

你将把代码分成两个文件:

  • config_loader.dart:创建一个 ConfigLoader 类,模拟异步加载配置设置。这个类应包含:
    • 三个 final 字段:appName(String)、version(String)和 maxUsers(int)
    • 一个用于初始化这三个字段的私有构造函数
    • 一个名为 load 的静态异步工厂方法,它接收一个 String configId 参数,并返回 Future<ConfigLoader>。这个方法应当:
      • 使用 Future.delayed(),并设置 100 毫秒的延迟来模拟加载所需的时间
      • 根据 configId 生成配置值:
        • appName:在 configId 后面拼接 " App"
        • version:在 "1.0." 后面接上 configId 的长度
        • maxUsers:configId 的长度乘以 10
      • 使用这些值返回一个新的 ConfigLoader 实例
    • 一个 displayInfo() 方法,按以下格式输出配置:Config: [appName] v[version] (max [maxUsers] users)
  • main.dart:导入配置加载器,并演示异步初始化模式:
    • 输出 Loading configurations...
    • 使用 configId "Production" 加载配置,并调用它的 displayInfo()
    • 使用 configId "Dev" 加载配置,并调用它的 displayInfo()
    • 输出 All configs loaded!

注意,静态 load 方法负责处理所有异步操作,而私有构造函数只负责赋值。调用方使用 await 来获取完全初始化的对象!

预期输出:

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

亲手试试

import 'config_loader.dart';

void main() async {
  // TODO: 打印 "Loading configurations..."
  
  // TODO: 加载 configId 为 "Production" 的配置
  // 并调用它的 displayInfo() 方法
  
  // TODO: 加载 configId 为 "Dev" 的配置
  // 并调用它的 displayInfo() 方法
  
  // TODO: 打印 "All configs loaded!"
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Dart 在线编译器