Synchrounous way to check if Future completed - dart

Is there is a synchronous way to check if a Future has completed in Dart?
Cheers!

No.
There is deliberately no way to check synchronously whether a future has already completed.
You can obviously keep track yourself:
Foo result = null;
fooFuture.then((value) { result = value; });
...
/// later
if (result != null) ...
That still only works after the first microtask, because your callback won't get called any sooner than that.

You can use Completer class.
final completer = Completer();
myFuture
.then(completer.complete)
.catchError(completer.completeError);
if (completer.isCompleted) {
// Future has completed
}

Related

dart - correct coding pattern for subscription when using null saftey?

I've enabled the dart 2.8 experimental null saftey.
I have the following exiting code.
StreamSubscription<String> subscription;
subscription =
response.transform(Utf8Decoder()).transform(LineSplitter()).listen(
(line) async {
result += line;
},
onDone: () async {
unawaited(subscription.cancel());
completer.complete(result);
},
);
With null saftey enabled I get a error in the 'onDone' method where it calls subscription.cancl
"The expression is nullable and must be null-checked before it can be used.
Try checking that the value isn't null before using it.",
I can fix the problem by putting a conditional before the call to cancel, but this seems unnecessary as in reality subscription can never be null.
Is there a coding pattern that allows subscription to be declared as non-null?
The problem here is that the read of subscription happens at a place where it's still potentially unassigned. It isn't, actually, but we only know that because the listen method promises not to call any of the callbacks before returning. The compiler can't see that. So, you need to move the reading to after the assignment.
What I'd do to make this listen call work:
var buffer = StringBuffer(result);
var subscription = response
.transform(Utf8Decoder())
.transform(LineSplitter())
.listen((line) {
buffer.write(line);
});
subscription.onDone(() {
completer.complete(buffer.toString());
});
I removed the async from the callbacks because it is not needed. All it does to make these functions async is to return a future that no-one would ever look at.
In general, the callbacks on Stream and Future should have non-async callbacks.
I also removed the subscription.cancel from the onDone event handler. If you get a "done" event, the subscription is done, there is no need to cancel it.
I also added a string buffer to avoid the quadratic time and space complexity of repeated string concatenation.
Looking at the code, you seem to be concatenating lines right after splitting them, maybe all you need is:
response.transform(Utf8Decoder()).join("").then(completer.complete);
I'll assume for now that the splitting+joining is necessary.
In that case, what I'd actually prefer to do instead is of using listen is:
var buffer = StringBuffer();
response
.transform(Utf8Decoder())
.transform(LineSplitter())
.forEach((line) {
buffer.write(line);
}).then(() {
completer.complete(buffer.toString());
}, onError: (e, s) {
completer.completeError(e, s);
});
or, if in an async function:
try {
var buffer = StringBuffer();
await for (var line in response.transform(Utf8Decoder()).transform(LineSplitter())) {
buffer.write(line);
}
completer.complete(buffer.toString());
} catch(e, s) {
completer.completeError(e, s);
}

how to wait for the completion of Future without 'async'

how to wait for the completion of Future without 'async' and 'futures'?
In the library that I use all functions are asynchronous.
// must return <bool>
bool my_func(int x){
//returns Future<int>
var tmp = somelib.somefunc( ... );
//wait y
return x == y;
}
I tried to write my 'await', but
waiting for a result with a while loop freezes everything.
dynamic my_await(Future f) {
dynamic x;
bool completed = false;
f.then((v){
x = v;
completed = true;
});
do {} while (!completed);
return x;
}
Dart VM version: 1.24.3 (Mon Dec 18 16:57:48 2017) on "linux_x64"
A synchronous function, or really, any Dart function, returns a value immediately when you call them. If you want to return a boolean immediately, and the value of that boolean depends on the result that some future completes with, then there is no way to compute that boolean in time.
If you need to wait for a future, then your function is asynchronous. You need to return something immediately, even if you don't know the result yet. That's what a Future is. It's not magical in any way, it's just an object that you can set a callback on which gets called when some result is ready.
So, you need to return a Future<bool> for this to work.
Dart is single-threaded. Without using isolates, there is no concurrency. Instead asynchronous functions work by taking turns, giving time for other code to run, e.g., while they wait on a future. If you just do a do {} while (!completed); then no other code gets to run, which means that nothing will be able to set completed to true.
I'm new to dart, so not sure if this is the correct way of doing it, but I've solved this issue by using the function whenCompleted() on the Future returned by the async method I'm calling.
Here openDatabase returns a Future.
abstract class IBaseDatabaseHandler {
Database sqliteDbHandler;
IBaseDatabaseHandler.sqlite(String dataBasePath) {
sqfliteFfiInit();
var databaseFactory = databaseFactoryFfi;
databaseFactory
.openDatabase(dataBasePath)
.whenComplete(() => sqliteDbHandler);
}
}

How to know if a certain future is complete by avoiding a chain of future as return types?

Scenario
If I want to read from a file and store the data in a Map, and if that map is being used multiple times for validation.
Is it possible for me to do this without having to change the return type of all methods, that use the above mentioned map, to Future?
Example:
Map metadata = null
Future readFromFile async {
.... metadata = await File.readingfromFile(...);
}
Future getRegion(..) async {
if(metadata == null) { await readFromFile() }
return metadata["region"]
}
Using the above code if a method(like isValidRegion,etc) that uses and needs getRegion(..) to complete, then the return type of isValidRegion should be converted to Future.
Future<bool> isValidRegion(..) async {
return ((await getRegionData(...)) != null )
}
If that isValidRegion is present within another methods, then the return type of them have to be changed to Future as well.
Future<String> parse(...) async {
....
if(await isValidRegion()) {
...
}
...
}
What is an elegant way to avoid this chain of futures as return types?
Async execution is contagious, there is nothing you can do to get back from async to sync execution.
What you can do is to do the read from the file synchronous to avoid the problem in the first place (if this is possible, if you read it from a network connection, this might not be possible).

indexed_db getObject() - how to return result

I would like to know how to define the data type and how to return the object (record) using getObject(). Currently, the only way that I have been able to use the result (record) outside of the function that obtains it is to call another function with the result. That way, the data-type does not need to be specified. However if I want to return the value, I need to define the data-type and I can't find what it is. I tried "dynamic" but that didn't appear to work. For example ":
fDbSelectOneClient(String sKey, Function fSuccess, String sErmes) {
try {
idb.Transaction oDbTxn = ogDb1.transaction(sgTblClient, 'readwrite');
idb.ObjectStore oDbTable = oDbTxn.objectStore(sgTblClient);
idb.Request oDbReqGet = oDbTable.getObject(sKey);
oDbReqGet.onSuccess.listen((val){
if (oDbReqGet.result == null) {
window.alert("Record $sKey was not found - $sErmes");
} else {
///////return oDbReqGet.result; /// THIS IS WHAT i WANT TO DO
fSuccess(oDbReqGet.result); /// THIS IS WHAT i'm HAVING TO DO
}});
oDbReqGet.onError.first.then((e){window.alert(
"Error reading single Client. Key = $sKey. Error = ${e}");});
} catch (oError) {
window.alert("Error attempting to read record for Client $sKey.
Error = ${oError}");
}
}
fAfterAddOrUpdateClient(oDbRec) {
/// this is one of the functions used as "fSuccess above
As someone else once said (can't remember who), once you start using an async API, everything needs to be async.
A typical "Dart" pattern to do this would be to use a Future + Completer pair (although there's nothing inherently wrong with what you've done in your question above - it's more a question of style...).
Conceptually, the fDbSelectOneClient function creates a completer object, and the function returns the completer.future. Then, when the async call completes, you call completer.complete, passing the value in.
A user of the function would call fDbSelectOneClient(...).then((result) => print(result)); to make use of the result in an async way
Your code above could be refactored as follows:
import 'dart:async'; // required for Completer
Future fDbSelectOneClient(String sKey) {
var completer = new Completer();
try {
idb.Transaction oDbTxn = ogDb1.transaction(sgTblClient, 'readwrite');
idb.ObjectStore oDbTable = oDbTxn.objectStore(sgTblClient);
idb.Request oDbReqGet = oDbTable.getObject(sKey);
oDbReqGet.onSuccess.listen((val) => completer.complete(oDbReqGet.result));
oDbReqGet.onError.first.then((err) => completer.completeError(err));
}
catch (oError) {
completer.completeError(oError);
}
return completer.future; // return the future
}
// calling code elsewhere
foo() {
var key = "Mr Blue";
fDbSelectOneClient(key)
.then((result) {
// do something with result (note, may be null)
})
..catchError((err) { // note method chaining ..
// do something with error
};
}
This future/completer pair only works for one shot (ie, if the onSuccess.listen is called multiple times, then the second time you will get a "Future already completed" error. (I've made an assumption on the basis of the function name fDbSelectOneClient that you are only expecting to select a single record.
To return a value from a single future multiple times, you'll probably have to use the new streams feature of the Future - see here for more details: http://news.dartlang.org/2012/11/introducing-new-streams-api.html
Note also, that Futures and Completers support generics, so you can strongly type the return type as follows:
// strongly typed future
Future<SomeReturnType> fDbSelectOneClient(String sKey) {
var completer = new Completer<SomeReturnType>();
completer.complete(new SomeReturnType());
}
foo() {
// strongly typed result
fDbSelectOneClient("Mr Blue").then((SomeReturnType result) => print(result));
}

Check if Future is complete

Before 'm3' you could check if a Future was completed with 'completer.future.isComplete' this seems to be gone. Is there a replacement? or do I need to save it myself then
(it seems inside the _CompleterImpl there is still a field '_isComplete' but its not exposed
With M3 Dart, it's best to just use your own flag.
future.whenComplete(() {
tweenCompleted = true;
});
Dart is a single threaded language so there is no race condition here.
Note that the [action] function is called when this future completes, whether it does so with a value or with an error.
An alternative to #Cutch's solution is to wrap the Future in a Completer:
Completer<T> wrapInCompleter<T>(Future<T> future) {
final completer = Completer<T>();
future.then(completer.complete).catchError(completer.completeError);
return completer;
}
Future<void> main() async {
final completer = wrapInCompleter(asyncComputation());
if (completer.isCompleted) {
final result = await completer.future;
// do your stuff
}
}
This approach is more resourceful since you can both await for the completion asynchronously and check whether the future is completed synchronously.
Using an extension on Future and building on Hugo Passos' answer:
extension FutureExtension<T> on Future<T> {
/// Checks if the future has returned a value, using a Completer.
bool isCompleted() {
final completer = Completer<T>();
then(completer.complete).catchError(completer.completeError);
return completer.isCompleted;
}
}

Resources