dart await on constructor - dart

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

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.

Related

Dart - How to add async to class? [duplicate]

Let's assume that an initialization of MyComponent in Dart requires sending an HttpRequest to the server. Is it possible to construct an object synchronously and defer a 'real' initialization till the response come back?
In the example below, the _init() function is not called until "done" is printed. Is it possible to fix this?
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");
}
Output:
done
init
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.
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.
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.

A value of type Future<Class> cannot be assigned to an entity of type Class

I want to be able to return a Class in my case the class name is Car. I want to return it from my provider to my screen. In my provider i have this method which should return the an object of the class Car provider.dart
Future<Car> fetchCarById(int carId) async {
final url =
'demourl';
try {
final response = await http.get(url);
final extractedCar = json.decode(response.body);
if (extractedCar == null) {
return null;
}
final Car selectedCar = Car(
id: extractedCar['ID'],
brand: extractedCar['Brand'],
model: extractedCar['Model'],
final List<Car> loadedProducts = [];
loadedProducts.add(selectedCar);
_items = loadedProducts;
notifyListeners();
return selectedCar;
} catch (error) {
throw error;
}
}
But instead I am getting this error
A value of type Future cannot be assigned to an entity of type Car
When i try to get the Car object from my screen screen.dart
final Car car = Cars(auth.token).fetchCarById(carId);
I saw it could be done with FutureBuilder, but is there a simplify way to do this? I don't want to touch my code in the screen.dart file. Is is possible?
You method is marked as async and does therefore not return Car but Future<Car>. In you assignment you have set the type to Car and there gets the error since there are a type mismatch between the type of the variable and the returned value of the called method.
The returned Future indicates that the value are properly not ready and you need to wait for the value. This can be done in a async method by using the await keyword:
Future<void> method() async {
final Car car = await Cars(auth.token).fetchCarById(carId);
}
Or you can add a callback method which are called when the value is ready:
void method() {
final Car car =
Cars(auth.token).fetchCarById(carId).then((car) => print(car));
}
I recommend you read the documentation about asynchronous programming in Dart: https://dart.dev/codelabs/async-await

How to properly call methods returning future in Reactor

To prevent the XY problem, I'll start from the beginning:
I have a non-blocking SOAP client which I wrapped it to make the return type Mono<T> (By default it accepts callback. I can elaborate on this if needed).
Now I want to do (given ID):
1. Get the code by ID
2. Do something with the code
3. After that, get Foo and Bar and create FooBar
What I wrote was:
public class MyService {
private final MySoapClient soapClient;
public Mono<FooBarDto> doSomething(String id) {
return Mono.just(id)
.flatMap(soapClient::getCode) // returns Mono<String>
.flatMap(code ->
soapClient.doSomething(code) // returns Mono<Void>
.then(getFooBar(id, code))); // See this
}
private Mono<FooBarDto> getFooBar(String id, String code) {
return Mono.zip(
soapClient.getFoo(code), // returns Mono<Foo>
soapClient.getBar(code) // returns Mono<Bar>
).map(tuple2 -> toFooBarDto(id, tuple2));
}
private FooBarDto toFooBarDto(String id, Tuple2<Foo, Bar> tuple2) {
return FooBarDto.builder()/* set properties */.build();
}
}
Now the problem is, because methods of the SOAP client are not lazy (the moment you call them they start the process), the semantic of then won't work here. Meaning I want to get Foo and Bar when doSomething is done. They all start together.
I tried to change it fix it by changing then to flatMap, but made it even worse. The getFooBar never got called. (1. Can someone please explain why?).
So what I ended up doing was to wrap SOAP calls again to make them lazy:
public class MySoapClient {
private final AutoGeneratedSoapClient client;
Mono<Foo> getFoo(GetFooRequest request) {
return Mono.just(request).flatMap(this::doGetMsisdnByIccid);
}
private Mono<Foo> doGetFoo(GetFooRequest request) {
val handler = new AsyncHandler<GetFooRequest>();
client.getFoo(request, handler);
return Mono.fromFuture(handler.future);
}
private static class AsyncHandler<T> implements javax.xml.ws.AsyncHandler<T> {
private final CompletableFuture<T> future = new CompletableFuture<>();
#Override
public void handleResponse(Response<T> res) {
try {
future.complete(res.get());
} catch (Exception e) {
future.completeExceptionally(e);
}
}
}
}
Is there any better way to do it? Specifically:
2. Using CompeletableFuture and the callback.
3. Making methods lazy in the SOAP client.
I tried to change it fix it by changing then to flatMap, but made it
even worse. The getFooBar never got called. (1. Can someone please
explain why?)
I think a Mono<Void> always completes empty (or error), so subsequent flatMap is never called.
Using CompeletableFuture and the callback.
Making methods lazy in the SOAP client.
To make the call lazy you can do one of the followings:
1, You can use Mono.fromFuture which accepts a supplier:
private Mono<Foo> doGetFoo(GetFooRequest request) {
return Mono.fromFuture(() -> {
val handler = new AsyncHandler<GetFooRequest>();
client.getFoo(request, handler);
return handler.future;
});
}
2, You can use Mono.defer:
private Mono<Foo> doGetFoo(GetFooRequest request) {
return Mono.defer(() -> {
val handler = new AsyncHandler<GetFooRequest>();
client.getFoo(request, handler);
return Mono.fromFuture(handler.future);
});
}
3, You can get rid of CompletableFuture and use Mono.create instead, something like this:
private Mono<Foo> doGetFoo(GetFooRequest request) {
return Mono.create(sink -> {
AsyncHandler<Foo> handler = response ->
{
try
{
sink.success(response.get());
} catch (Exception e)
{
sink.error(e);
}
};
client.getFoo(request, handler);
});
}
If you do any of these it will be safe to use then method and it will work as expected.

Initialize class with asynchronous variable

How can I initialize a class with asynchronous variables so that they are set before the class is used? I have my class currently just calling an async init function but I would have to call that separately to wait for it to finish:
class Storage {
String imageDirectory;
String jsonDirectory;
SharedPreferences instance;
String uuid;
init() async {
imageDirectory = '${(await getApplicationDocumentsDirectory()).path}/image_cache/';
jsonDirectory = '${(await getApplicationDocumentsDirectory()).path}/json_cache/';
instance = await SharedPreferences.getInstance();
uuid = instance.getString("UUID");
}
}
Is there a better way to do this?
You might hope that you could have async factory constructors, but they aren't allowed.
So one solution is a static getInstance(), for example:
class Storage {
static Future<Storage> getInstance() async {
String docsFolder = (await getApplicationDocumentsDirectory()).path;
return new Storage(
docsFolder + '/image_cache/',
docsFolder + '/json_cache/',
(await SharedPreferences.getInstance()).getString('UUID'));
}
String imageDirectory;
String jsonDirectory;
String uuid;
Storage(this.imageDirectory, this.jsonDirectory, this.uuid);
}
You could pass parameters into getInstance and thus into the constructor, as required. Call the above with:
Storage s = await Storage.getInstance();

Mocking with Dart - How to test that a function passed as a parameter was called?

Admittedly, this is a little bit of an odd test case, but it's a problem I've ran in to. I have a class that takes a function as a parameter in it's constructor. I'd like to know if the function that was passed was called. Here's an example:
class TestClassMock extends Mock implements RealClass {
RealClass _real;
TestClassMock() {
_real = new RealClass();
when(callsTo("myNamedFunction")).alwaysCall(_real.myNamedFunction);
}
}
class RealClass {
String _name = "RealClass";
Function myNamedFunction;
RealClass() {
myNamedFunction = _theNamedFunction;
}
String _theNamedFunction() {
return _name;
}
}
class ClassThatCallsRealClass {
ClassThatCallsRealClass(Function func) {
func();
}
}
//The test
TestClassMock testClassMock = new TestClassMock();
ClassThatCallsRealClass caller = new ClassThatCallsRealClass(testClassMock.myNamedFunction);
testClassMock.getLogs(callsTo("myNamedFunction")).verify(happenedOnce);
So to explain a bit, ClassThatCallsRealClass takes a function as a parameter and calls it. If you were to pass in (Instance Of RealClass).myNamedFunction, this would in turn call the private function _theNamedFunction on RealClass. However, if you try to mock RealClass and redirect all calls from myNamedFunction to the RealClass myNamedFunction, this seems to fail. I don't see any clear way to get this to work, but I would think it'd be possible.
Any ideas?
In Dart, all functions are instances of class Function as you know since you pass an instance of Function to the ClassThatCallsRealClass constructor. Instances of Function have a method call() as shown here.
Meanwhile, Dart has a very good mocking capability described here (with thanks to #KWalrath for the update).
So all you need to do is test with mocks like with any other object. Just as described in the reference, create a spy for ClassThatCallsRealClass and a mock for your Function instance. Then use a verify(happenedOnce) on the call() method of the function.
To mock your function do this:
class MockFunction extends Mock {
call(int a, int b) => a + b;
}
var mock = new MockFunction();
mock(1,2); //returns 3
Of course the parameter list to call will match that of the real function. Pass mock to your spy on ClassThatCallsRealClass.
That worked for me:
library x;
import "package:unittest/unittest.dart";
import "package:unittest/mock.dart";
class TestClassMock extends Mock implements RealClass {
RealClass _real;
TestClassMock() {
_real = new RealClass();
when(callsTo("myNamedFunction")).alwaysCall(_real.myNamedFunction);
}
}
class RealClass {
String _name = "RealClass";
Function myNamedFunction;
RealClass() {
myNamedFunction = _theNamedFunction;
}
String _theNamedFunction() {
return _name;
}
}
class ClassThatCallsRealClass {
ClassThatCallsRealClass(Function func) {
func();
}
}
class MyFunc implements Function {
Function func;
String functionName;
MyFunc(this.func, this.functionName);
call() {
var inv = new MyInvocation(functionName);
func(inv);
}
}
main(List<String> args) {
test('xx', () {
//The test
TestClassMock testClassMock = new TestClassMock();
ClassThatCallsRealClass caller = new ClassThatCallsRealClass(new MyFunc(testClassMock.noSuchMethod, "myNamedFunction"));
testClassMock.getLogs(callsTo("myNamedFunction")).verify(happenedOnce);
});
}
class MyInvocation extends Invocation {
final String f;
MyInvocation(this.f);
bool get isGetter => false;
bool get isMethod => true;
bool get isSetter => false;
Symbol get memberName => new Symbol(f);
Map<Symbol, dynamic> get namedArguments => {};
List get positionalArguments => [];
}
testClassMock.myNamedFunction returns null so I call noSuchMethod directly instead which needs an Invocation.
Invocation is abstract so I created an implemented.
MyFunc is a class that wraps the function. MyFunc can be called as a function because it implements the call method.

Resources