-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Allow async constructors (or at least factory constructors) #23115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Asynchronous constructors makes little sense - a constructor must return an instance of the class it's a constructor of, and an async function must return a Future. What you can do is to have static a function that returns a Future, instead of having a factory constructor. Naming isn't as easy when you can't rely on a "new" in front of the expression. I have so far used "create" as part of the name when no other name seemed reasonable. Added Area-Language, Triaged labels. |
This comment was originally written by [email protected] @LRN: If you look at the request, you'll see that I mentioned that I ended up using a static method for construction instead of doing the logic in the constructor. While this works, it seemed odd that I had to write a static factory method instead of being able to use a factory constructor. I used the name getInstance, but create would be fine as well. What really sucks is that I'm now discouraged from using factory constructors in my code for two reasons:
I personally don't see any readability or usage issues with something like: Foo foo = await new Foo(); or even: new Foo().next((foo){ I think this would be a great feature to add to the rich level of async support that Dart already has. If it helps, I'm more than willing to have this thread focus only on factory constructors, since that's what I really wanted to use. @andrew: Like any project, I would say that what's next should be based on user feedback. I don't wish to get into a full argument about operators and recursion here unless someone thinks it'll add value to discussing this feature. If you'd like, I'd gladly take this discussion to the mailing list, as I have responses to at least some of your questions and concerns. |
This comment was originally written by [email protected] Simple case: I have an object with expensive logic for creating its initial state. It could be file I/O, network requests, data crunching, etc. I don't want the user to use the object until that expensive logic if finished, and I don't want to block while it's being executed. class Foo { var expensiveResult; //Also not allowed :( Foo._internal() { Future _expensive() async { main() { |
This comment was originally written by @kaendfinger Why not just assign a completer in the constructor, and ask the user to do something like instance.onReady class MyClass { MyClass() { main() async { Imho, you should never do anything expensive in a constructor. Constructing objects should generally be fast. However, for factory constructors, I do see the benefit. What if you load objects from a database? What then do you do with factory constructors? |
This comment was originally written by [email protected] I can definitely see an argument against doing this for non-factory constructors. What really prompted this enhancement request was the inability to use factory constructors for all use cases that traditional factories can cover. I also agree that, in general, you should never have anything expensive inside a constructor, which is why I ended up using a factory method. Right now, factory constructors support complex initialization logic as long as all of that logic is synchronous. Your approach of exposing a property is a potential solution, but at that point I could just force the user to call the actual async method itself. The goal was to make initializing the default state as consumer-friendly as possible. |
I try to implements import 'dart:async';
import 'dart:mirrors';
@proxy
class AsyncFact implements Future {
factory AsyncFact() {
return new AsyncFact._internal(new Future.delayed(
const Duration(seconds: 1), () => '[Expensive Instance]'));
}
AsyncFact._internal(o) : _mirror = reflect(o);
final InstanceMirror _mirror;
@override
noSuchMethod(Invocation invocation) => _mirror.delegate(invocation);
}
main() async {
print(await new AsyncFact());
} |
@Ticore: I'm not sure what you are asking for here. Your code already returns a future (an AsyncFact even) which you can "await" on. You can't mark your factory constructor async because even if it worked, it would return a system Future, not an AsyncFact, so it's not a valid return value for a constructor on AsyncFact. |
+1 for async factory constructors. Was my naive approach for constructing a database-backed object for a web-app; didn't work, so everything since then on it has been 'work-around' time for me. Perhaps ideological considerations out-weigh this, but there is justification on the 'batteries included' side of things. |
It's not impossible to add to the language, but it will break some of the rules that are generally followed. The constructor would have to be a factory constructor anyway. Maybe it should be using
or
|
+1 for async support in factory constructors in order to allow construction of objects with database data and avoid the need to call the async method for complete the object. |
Is the way in which you create a database dependent objects still with a separate method? If so I share @zidenis thought on async in factory constructors. |
Now that class A {
factory A.a() => ...
}
class B {
static B b() => ...
}
void main() {
A.a();
B.b();
} ... and of course, import 'dart:async';
class C {
static Future<C> load(String url) => ...
} @lrhn Unless you see this happening (re-open in that case), let's close this. |
This class can be initialized with a username / password combo, which it uses to get a token and stores that token. This token will be used for all other methods / requests it provides. Dart does not support async factory constructors (see dart-lang/sdk#23115) so we use a static method instead. What is interesting is that since all of this depends on a Network request, all the steps in the pipeline return a Future. However, at some point, we're going to need an actual instance before we can proceed. Will be interesting to see how we get there.
@matanlurey That's true, with a small caveat: the default constructor name (the best name, sadly) can't be used for static methods. This doesn't work: import 'dart:async';
class C {
static Future<C>(String url) => ...
}
var c = await C('http://google.com'); |
Is it really going to end here? I'm amazed that not a lot of people are encouraging this :/ I've come across quite a few moments now where it really could have made everything smoother, like a Settings class with data from your "dynamic" json file for example. |
@matanlurey why is this closed?? I started learning dart 2 days ago and I already need async factory constructors. This came super natural. why is this so unwanted? |
The same. I use flutter sdk and I need to call async code within constructor: |
@rajadain could you reopen? |
@konsultaner sorry mate, not up to me, but dart-lang/language#782 looks promising. |
I wrote this test code using dart2.7 and flutter 1.20 version, which can be implemented in two ways. support flutter project. import 'dart:async';
import 'dart:math';
import 'package:meta/meta.dart';
mixin AsyncInitMixin<T extends Future> implements Future {
bool _isReady;
Future<T> _onReady;
bool get isReady => _isReady ?? false;
Future<T> get onReady => _onReady ??= _init();
Future<T> _init() async {
await init();
_isReady = true;
return this as T;
}
@override
Stream<T> asStream() => onReady.asStream();
@override
Future<T> catchError(Function onError, {bool Function(Object error) test}) => onReady.catchError(onError, test: test);
@override
Future<R> then<R>(FutureOr<R> Function(T value) onValue, {Function onError}) =>
onReady.then(onValue, onError: onError);
@override
Future<T> timeout(Duration timeLimit, {FutureOr Function() onTimeout}) =>
onReady.timeout(timeLimit, onTimeout: onTimeout);
@override
Future<T> whenComplete(FutureOr<void> Function() action) => onReady.whenComplete(action);
@protected
Future<void> init();
}
class Test with AsyncInitMixin {
String data;
Test({this.data});
@override
Future<void> init() async {
print('init: A ${DateTime.now()}, now data is $data');
await Future.delayed(Duration(seconds: 1));
data = '${Random().nextInt(99999999)}';
print('init: B ${DateTime.now()}, new data is $data');
}
}
main() async {
print('test1 await with constructor');
final Test test1 = await Test(data: 'aaa'); // await constructor
await test1;
print('test1 result is ${test1}, and data is ${test1.data}');
print('--=--=--=--=--=--');
print('test2 await with onReady');
final Test test2 = Test(data: 'bbb');
await test2;
await test2.onReady;
print('test2 result is ${test2}, and data is ${test2.data}');
print('=-=-=-=-=-=-=-=-=');
print('test1 await with onReady, ensure no reinit');
await test1.onReady;
await test1.onReady;
await test1.onReady;
print('test1 result is ${test1}, and data is ${test1.data}');
} The following is the console output log
|
Hi, thanks for the structure! I have a question. I have to use this with a named constructor. How I can do it? Thanks in advance. |
I disagree with this. Having work, especially async in constructors is bad practice because it can be hard to unit test. Since there is an
|
This issue was originally filed by [email protected]
What steps will clearly show the issue / need for enhancement?
What is the current output?
A warning from the analyzer stating that constructors cannot be marked async
What would you like to see instead?
The ability to mark a constructor async. Or at the very least, allow factory constructors to be marked as async. I ran into a situation where I needed to do some post construction logic using an internal method on the constructed object. The method I wanted to use is an asynchronous method and cannot be changed. I ended up using a "static factory method" along the lines of Class.getInstance(), but it seems like this is a situation that would be ideal for a factory constructor.
What version of the product are you using? On what operating system?
1.9.1 on OSX
Please provide any additional information below.
Example:
class Foo {
//Not allowed :(
Foo() async {
await _bar();
}
Foo._internal() {
//normal construction logic
}
//Also not allowed :(
factory Foo.factory() async {
Foo foo = new Foo._internal();
await foo._bar();
return foo;
}
//Works! But not ideal :(
static Future<Foo> getInstance() async {
Foo foo = new Foo._internal();
await foo._bar();
return foo;
}
Future _bar() async {
//do some expensive work asynchronously
}
}
The text was updated successfully, but these errors were encountered: