dart await on constructor
Asked Answered
E

1

9

What pattern should I use in this example to load and process some data. As value returns a value, it's not acceptable to have d as a Future. How can I get the constructor to wait until load has completed before continuing?

void main() {
    var data = new Data(); // load data
    print(data.value()); // data.d is still null
}

class Data {
    String d;
    Data() {
        load();
    }

    Future<void> load() async {
        d = await fn(); // some expensive function (e.g. loading a database)
    }

    String value() {
        return d;
    }
}
Envelope answered 6/2, 2019 at 8:22 Comment(2)
why don't you call the load function from mainMalapropism
Are you sure that it's blocking? I'm trying it in my code now but it seems like it still has "d" as null when I run the second method.Envelope
S
28

You cannot make a constructor asynchronous. An asynchronous function needs to return a Future, and a constructor needs to return an instance of the class itself. Unless the class is a future, the constructor cannot be asynchronous (and even then, it's not really the same thing, and you can't use async/await).

So, if your class needs asynchronous set-up, you should provide the user with a static factory method instead of a constructor. I'd usually hide the constructor then.

class Data {
  String _d;
  Data._();
  static Future<Data> create() async {
    var data = Data._();
    await data._load();
    return data;
  }
  Future<void> _load() async {
    _d = await fn(); 
  }
  String get value => _d;
}

As an alternative design, I wouldn't even put the load method on the class, just do the operation in the static factory method:

class Data {
  String _d;
  Data._(this._d);
  static Future<Data> create() async => Data._(await fn());
  String get value => _d;
}

Obviously other constraints might require that load has access to the object.

Sailboat answered 6/2, 2019 at 10:28 Comment(5)
That first method looks really concise and is probably exactly what I'm looking for. Syntax wise, is Data._() just an empty constructor that we're using to prevent it being called elsewhere?Envelope
Yes, Data._(); is just an empty private constructor, so nobody else can create instances of Data without also calling load. If we didn't write it, a default public constructor would be added.Sailboat
@Sailboat could you help me on this link? https://stackoverflow.com/q/56512200/1830228 thanksDarter
I thought working with statics was a no-go and should be avoided if possibleCodify
Static functions (top-level or inside classes/mixins/extensions) are perfectly fine to use. It's a design question when to use one or the other, when to make a function static instead of a constructor or an extension method, but there are definitely use-cases where it's the right choice. An asynchronous factory function is definitely a good candidate.Sailboat

© 2022 - 2024 — McMap. All rights reserved.