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);
}
}
Related
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);
}
I am just trying to understand how timeout works in Futures, but I can't interrupt anything a litle more complex. Look at this really simple code , I thought that it would get an exception if that loop take longer than 1 second but it does not happen. Anyone could please explain what happens here?
I am trying using this tool: https://dartpad.dartlang.org/
import 'dart:async';
void main() {
new Future(() {
var sum = 0;
for (var i = 0; i < 500000000; i++) {
sum += i;
}
return sum;
}).timeout(new Duration(seconds: 1)).then(print).catchError(print);
}
First of all Future.timeout does not interrupt any computation.
It creates a new Future which completes either with the value of the original Future, or with the result of timing out, if the original future didn't complete in time. It's more like it takes two futures: The original one and one created using Future.delayed, and then creates a third Future which will complete with the result of the first the other two to complete.
There is no signal going back to the computation which will eventually complete the original future. It will not stop unless you make it stop.
The use-case for Future.timeout is, say, a network connection that doesn't appear to give a result in time. You can't stop that request, but you can stop waiting for the answer, which is what timeout does.
What happens in your example is:
You create a Future, f1 with the Future constructor. This schedules a timer with zero duration to call the argument function.
You then call timeout on f1. This starts a timer with a duration of one second, and returns a future f2.
You then call then on f2 with print as argument. This puts a listener on f2 and returns a future f3.
You then call catchError on f3 which puts an error listener on f3 and returns a future f4 (which is then ignored).
Then control returns to the event loop, and the next event is the zero-duration timer.
The argument to the Future constructor is called. It counts to 500000000, then returns a value v and completes f1 with that.
The result of f1 is propagated to f2 which also completes with v.
The result of f2 is printed by print, and f3 completes with null.
The result of f4 is ignored by the catchError and f4 completes with null.
Then control returns to the event loop. The next event is the 1-second timer started by Future.timeout. More than one second may have passed already, but this is the earliest opportunity for it to run.
The Future.timeout timer callback sees that f2 is already completed and does nothing.
Control returns to the event loop, and since it is empty, the program ends.
i think you need to call this way timeout(Duration timeLimit, onTimeout());
so timeout(new Duration(seconds: 1),printFunction())
import 'dart:async';
_onTimeout() => print("Time Out occurs");
void main() {
new Future(() {
var sum = 0;
for (var i = 0; i < 500000000; i++) {
sum += i;
}
return sum;
}).timeout(new Duration(seconds: 1),onTimeout: _onTimeout());
}
Your code is not working because your future value is being processing synchronously, so the event loop cannot process anything else but the for loop you're running. I suggest to read through these articles to understand better https://medium.com/hackernoon/are-futures-in-dart-threads-2cdc5bd8063a https://medium.com/dartlang/dart-asynchronous-programming-isolates-and-event-loops-bffc3e296a6a of Futures and the event loop works.
A quick fix to your issue would be to use dart Isolates (together with the package:isolate library), for example:
import 'dart:async';
import 'package:isolate/isolate.dart';
Future<void> main() async {
var isolate = await IsolateRunner.spawn();
var value = await isolate.run(func, null, timeout: Duration(seconds: 1));
print(value);
}
int func(_) {
var sum = 0;
for (var i = 0; i < 500000000; i++) {
sum += i;
}
return sum;
}
It will throw a TimeoutException if the func() takes more than 1 second to complete, you can set an onTimeout function to be run when the timeout is hit.
I am using this function to add a timeout callback (repeated) to a specific GMainContext.
guint GstThreadHelper::timeoutAdd(guint delay, GSourceFunc function, gpointer data) {
// See https://developer.gnome.org/programming-guidelines/stable/main-contexts.html.en#implicit-use-of-the-global-default-main-context
// It is important that all thread functions we invoke don't implicitly decide a maincontext.
// We must manually provide one.
GSource *source = NULL;
guint id;
source = g_timeout_source_new(delay);
g_source_set_callback (source, function, data, NULL);
id = g_source_attach (source, priv->mainContext);
g_source_unref (source);
return id;
}
Later, I use the returned id to cancel the callback.
void GstThreadHelper::timeoutRemove(guint id) {
g_source_remove(id);
}
However, the callback still gets called. Here is my callback.
static gboolean position_update (gpointer user_data)
{
Player::PrivateData* priv = (Player::PrivateData*)user_data;
gint64 pos = 0;
if (gst_element_query_position (priv->playbin, GST_FORMAT_TIME, &pos)) {
pos = pos / 1000000;
priv->callback->PositionChanged(pos);
}
// Call me again
return TRUE;
}
I understand I am returning TRUE, but my understanding is that it still should be stopped. If I cancel callbacks by returning FALSE, I wouldn't bother with the g_source_remove call.
Why doesn't g_source_remove stop my callback from being raised?
EDIT
If I replace my timeoutAdd method with this...
guint GstThreadHelper::timeoutAdd(guint delay, GSourceFunc function, gpointer data) {
return g_timeout_add(delay, function, data);
}
...it works. However, I can't use this, because it doesn't trigger the callbacks on a specific GMainContext, as opposed to the default global GMainContext.
EDIT2
I copied the default source for g_timeout_add_seconds_full into my function, and it worked.
However, the moment I changed g_source_attach to use my private GMainContext, it failed.
The issue is something to do with calling g_source_remove for timeouts added on non-default GMainContexts.
It turns out that g_source_remove operates under the assumption that you are using the global/default GMainContext, which in this case, I am not.
I don't remember reading this in the docs.
Anyways, Here is the solution.
void GstThreadHelper::timeoutRemove(guint id) {
GSource* source = g_main_context_find_source_by_id(priv->mainContext, id);
if (source)
g_source_destroy (source);
}
This is essentially what g_source_remove is doing, but instead using our private GMainContext.
I was writing a function in dart that would delete an object from a browser-side Indexed DB, when I discovered that I had to return an outer function value from within an inner function:
Future<bool> delete() {
Transaction tx = db.transactionStore(storeName, "readwrite");
ObjectStore os = tx.objectStore(storeName);
os.delete(_key); // returns blank future, modifies tx
// This is not correct, but shows the idea:
if (tx.onComplete) {return true;}
if (tx.onError) {return false;}
}
This function is a method for a class that I am using to save and load to the Indexed DB.
I want this function to return true or false, or a Future object containing the same, when the delete operation succeeds or fails. However, the bottleneck is the os.delete(_key); statement: it returns a future, but the actual success or failure of the delete operation is provided by tx.onComplete and tx.onError. Both of these Objects are streams, so I need to create anonymous functions that handle events from them:
tx.onComplete.listen((e){
return_to_outer_function(true);
});
tx.onError.listen((e){
return_to_outer_function(false);
});
return_to_outer_function(bool) {
return bool; // doesn't work
}
As you can see, when I create anonymous functions, the return statement no longer completes the method, but the inner function. I could have the inner functions call other functions, but then those other functions have return statements of their own that don't return a result to the whole method.
I tried the approach of setting temporary variables and periodically checking them, but it's a very inelegant solution that I don't want to have to use, not just for potential bugs, but because it would hog up the single threaded event loop.
Is it possible to return a value to an outer function from an inner function? Or is there some other, better way to get a value from the presence or absence of events from a set of streams? Or is there another way of using IndexedDB that will avoid this problem?
You can use a Completer for this.
Future<bool> delete() {
Completer completer = new Completer();
Transaction tx = db.transactionStore(storeName, "readwrite");
ObjectStore os = tx.objectStore(storeName);
tx.onError.first((e){
//return_to_outer_function(false);
completer.complete(false);
});
tx.onComplete.first(bool) {
//return bool; // doesn't work
completer.complete(true)
}
os.delete(_key); // register listeners and then do delete to be on the save side
return completer.future;
}
you then call it like
delete().then((success) => print('succeeded: $success'));
see also https://api.dartlang.org/apidocs/channels/be/dartdoc-viewer/dart:async.Completer
Suppose our goal is to build a List which we will call storedResult.
We have a Future<List> returning function called multiple times:
Future<List> getList()...
Suppose that we will call our getList() function for the unknown multiple times and want to store the result of each List into our storedResult:
List storedResult = [];
someOtherList.forEach((element)){
getList().then((resultingList)){
storedResult.addAll(resultingList);
}
}
print(storedResult);
If we run this code, it will print as []. How do we express this so that the result is rather:
[resultingList, resultingList, ..., ]
Remember that Futures return immediately, but their associated callbacks run asynchronously. Specifically, the work that getList() does (and everything that then does) do not happen until execution returns to the event loop.
Your problem is that you're trying to print the result of an asynchronous operation synchronously, and that won't work.
If you want to use the stored result, then what you should do is something like:
Future<List<List>> globFutures(List someOtherList) {
List<Future> futures = [];
someOtherList.forEach((element) {
futures.add(getList());
});
// Automatically completes to a List containing all the
// results of all of the futures.
return Future.wait(futures);
}
main() {
var storedResults = [];
globFutures(someOtherList)
.then((List<List> results) {
storedResults = results;
// Run code in here that depends on storedResults being available.
print(storedResults);
});
// Do **NOT** use storedResults here, as it will be [].
// The Future functions have not yet run!
assert(storedResults == []);
};