在 Dart 中从构造函数调用异步方法

假设 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
66487 次浏览

A constructor can only return an instance of the class it is a constructor of (MyComponent). Your requirement would require a constructor to return Future<MyComponent> which is not supported.

You either need to make an explicit initialization method that needs to be called by the user of your class like:

class MyComponent{
MyComponent();


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


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

or you start initialization in the consturctor and allow the user of the component to wait for initialization to be 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");
}

When _doneFuture was already completed await c.initializationDone returns immediately otherwise it waits for the future to complete first.

Probably the best way to handle this is with a factory function, which calls a private constructor.

In Dart, private methods start with an underscore, and "additional" constructors require a name in the form ClassName.constructorName, since Dart doesn't support function overloading. This means that private constructors require a name, which starts with an underscore (MyComponent._create in the below example).

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");
}

This way, it's impossible to accidentally create an improperly initialized object out of the class. The only available constructor is private, so the only way to create an object is with the factory, which performs proper initialization.

I agree, an asynchronous factory function would help Dart devs with this problem. @kankaristo has IMHO given the best answer, a static async method that returns a fully constructed and initialized object. You have to deal with the async somehow, and breaking the init in two will lead to bugs.