【问题标题】:Calling an async method from component constructor in Dart从 Dart 中的组件构造函数调用异步方法
【发布时间】:2016-08-13 14:38:18
【问题描述】:

假设在 Dart 中初始化 MyComponent 需要向服务器发送一个 HttpRequest。是否可以同步构造一个对象并将“真正的”初始化推迟到响应返回?

在下面的示例中,在打印“done”之前不会调用 _init() 函数。有办法解决吗?

import 'dart:async';
import 'dart:io';

class MyComponent{
  MyComponent() {
    _init();
  }

  Future _init() async {
    print("init");
  }
}

void main() {
  var c = new MyComponent();
  sleep(const Duration(seconds: 1));
  print("done");
}

输出

done
init

【问题讨论】:

  • 你可以使用静态异步方法吗?
  • 如果不使用'await',它应该如何等待?

标签: asynchronous constructor dart


【解决方案1】:

构造函数只能返回它作为构造函数的类的实例 (MyComponent)。您的要求将要求构造函数返回不支持的 Future<MyComponent>

您需要创建一个需要由您的类的用户调用的显式初始化方法,例如:

class MyComponent{
  MyComponent();

  Future init() async {
    print("init");
  }
}

void main() async {
  var c = new MyComponent();
  await c.init();
  print("done");
}

或者您在构造函数中开始初始化并允许组件的用户等待初始化完成。

class MyComponent{
  Future _doneFuture;

  MyComponent() {
    _doneFuture = _init();
  }

  Future _init() async {
    print("init");
  }

  Future get initializationDone => _doneFuture
}

void main() async {
  var c = new MyComponent();
  await c.initializationDone;
  print("done");
}

_doneFuture 已经完成时,await c.initializationDone 立即返回,否则它等待未来先完成。

【讨论】:

    【解决方案2】:

    处理这个问题的最好方法可能是使用工厂函数,它调用私有构造函数。

    在 Dart 中,私有方法以下划线开头,“附加”构造函数需要ClassName.constructorName 形式的名称,因为 Dart 不支持函数重载。这意味着私有构造函数需要一个以下划线开头的名称(以下示例中为MyComponent._create)。

    import 'dart:async';
    import 'dart:io';
    
    class MyComponent{
      /// Private constructor
      MyComponent._create() {
        print("_create() (private constructor)");
    
        // Do most of your initialization here, that's what a constructor is for
        //...
      }
    
      /// Public factory
      static Future<MyComponent> create() async {
        print("create() (public factory)");
    
        // Call the private constructor
        var component = MyComponent._create();
    
        // Do initialization that requires async
        //await component._complexAsyncInit();
    
        // Return the fully initialized object
        return component;
      }
    }
    
    void main() async {
      var c = await MyComponent.create();
    
      print("done");
    }
    

    这样,就不可能在类之外意外地创建一个未正确初始化的对象。唯一可用的构造函数是私有的,因此创建对象的唯一方法是使用工厂,它执行正确的初始化。

    【讨论】:

    • 这也对发送者隐藏了对象创建的细节
    • 我认为这是一个更好的解决方案。唯一的缺点是,如果您启用 null 安全性并且如果您有一些异步初始化的变量,则必须将它们声明为可为 null。这有点烦人。有什么办法可以避免这种情况吗?
    • 您可以将late 关键字与您的属性@Marco 一起使用
    • 这是一个更好的答案,因为它简化了对象的创建。无需被工厂模式吓到。
    猜你喜欢
    • 2022-11-06
    • 2017-10-18
    • 2015-05-17
    • 2014-02-16
    • 2014-05-27
    • 2019-12-11
    • 2020-07-28
    相关资源
    最近更新 更多