I have a setMethodCallHandler which runs a callback from my Java code, and I want it to set a Future containing the result. Something like
Future<String> fun() async {
return setMethodCallHandler((MethodCall call) {
return () async {return call.arguments["arg"];}();
});
}
What I want to be able to do is if call.argument will return "abc",
var a = await fun();
a will be equal to "abc"
The only information I found was the documentation on setMethodCallHandler:
If the future returned by the handler completes with a result, that value is sent back to the platform plugin caller wrapped in a success envelope as defined by the codec of this channel. If the future completes with a PlatformException, the fields of that exception will be used to populate an error envelope which is sent back instead.
But I don't understand how can I get the "platform plugin caller wrapped in a success envelope as defined by the codec of this channel"?
I'm guessing, but correct me if I'm wrong, that setMethodCallHandler does not return a value, it only sets up a function to be called later.
So, you can't use the return value of the setMethodCallHandler for anything.
That effectively means that you have an "event" of a kind, one that you want to "convert" into a future completion. To do that, you use a Completer to create and later complete a Future.
Future<String> fun() {
var completer = new Completer<String>();
setMethodCallHandler((MethodCall call) {
completer.complete(call.arguments["arg"]);
}
return completer.future;
}
Using async functions works when your events all come from futures or streams, but when you get other kinds of events (like port events, I/O callbacks or timers) and you want to map that back to future/stream events, you use either a Completer to complete a future or a StreamController to add events to a stream.
Related
I have a confusion in understanding Dart's async/await functionality. Most tutorials use the following example:
Future<String> createOrderMessage() async {
var order = await fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
// Imagine that this function is
// more complex and slow.
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);
Future<void> main() async {
print('Fetching user order...');
print(await createOrderMessage());
}
I understand that the fetchUserOrder function returns a Future. Anyone who calls this function will immediately get a Future object as a result but this Future object may not have the real result value at that time. It will be filled up in "future".
Now, the createOrderMessage function calls: await fetchUserOrder(); This means, the control will wait till fetchUserOrder() returns a completed/non-empty Future. Am I right here? Is this what await does?
If that is correct, it means, the createOrderMessage function is essentially synchronous now because it won't return until the Future returned by fetchUserOrder is filled. Thus, the order variable in this function is initialized with a completed Future. Then why does this function need the "async" keyword in its function declaration? What does it signify?
Second confusion is if Dart is single threaded, when and how exactly is an async function executed? If I invoke multiple method calls that return Futures like this without await:
...
Future<A> fa = getA();
Future<B> fb = getB();
Future<C> fa = getC();
...
Will the functions getA, getB, and getC be executed sequentially in the background or they will be executed "in parallel" somehow?
Now, the createOrderMessage function calls: await fetchUserOrder(); This means, the control will wait till fetchUserOrder() returns a completed/non-empty Future. Am I right here? Is this what await does?
Yes. the await keyword means you get the completed T instead of Future<T> in return. So:
var a = fetchUserOrder(); // Type of a: Future<String>
var b = await fetchUserOrder(); // Type of b: String
When you use await, you tell the code to await the completion of the process and get the actual result. But without await, the stream is in the future and is in process until you await it somewhere else.
why does this function need the "async" keyword in its function declaration?
It's a rule to specify long-running functions. So when a function awaits for a long running process to complete, the function itself is also a long-running process. Why? Because it's waiting for another one to complete.
Therefore, you need to specify async for that as well. When another function is calling this one, it also knows this one might be long-running.
In short, every awaitable function needs to be in an async function.
Second confusion is if Dart is single threaded, when and how exactly is an async function executed?
You're mixing the concept of parallelism with asynchronous. An async process does not have to be done in multi thread.
Async processes can be one one at time, but in a different order in compare to synchronous code.
Take a look at this article which explains asynchronous in a single thread.
Now, the createOrderMessage function calls: await fetchUserOrder(); This means, the control will wait till fetchUserOrder() returns a completed/non-empty Future. Am I right here? Is this what await does?
If that is correct, it means, the createOrderMessage function is essentially synchronous now because it won't return until the Future returned by fetchUserOrder is filled. Thus, the order variable in this function is initialized with a completed Future.
await allows asynchronous functions to have the appearance of synchronous functions by allowing asynchronous code to be structured very similarly to synchronous code. In your example, it's syntactic sugar for registering a Future.then() callback and immediately returning:
Future<String> createOrderMessage() {
var future = fetchUserOrder().then((order) {
return 'Your order is: $order';
});
return future;
}
await defers execution of the rest of that function; it does not wait and block execution of your entire thread. Your program will continue running its event loop, possibly allowing other asynchronous operations to make progress.
Will the functions getA, getB, and getC be executed sequentially in the background or they will be executed "in parallel" somehow?
As I mentioned in comments, it depends on how getA, getB, and getC are implemented. They could run in parallel, such as if they run in separate Dart isolates or in separate threads in the Dart VM/runtime.
It's almost like so I can write an async that is intended not to be awaitable outside by designating it as, say, void not Future<void>
void fetchUserOrder() async {
// Imagine that this function is fetching user info from another service or database.
await Future.delayed(Duration(seconds: 2));
print('Large Latte');
print('After Large Latte');
}
void main() async {
fetchUserOrder();
await Future.delayed(Duration(seconds: 1));
print('Fetching user order...');
print('After Fetching user order...');
}
Print:
Fetching user order...
After Fetching user order...
Large Latte
After Large Latte
This is sooo so unallowed in .NET and also c++20 realm.
I wonder why dart let async functions return non-Future?
Wouldn't this end up with intended hidden async in API all over the places?
Dart doesn't force you to await futures. Futures existed before await was introduced, so it couldn't really.
That means that you can always not await a future. There are lints to make it harder to avoid, but nothing prevents you from just defining ignore(Object? _){} and doing ignore(futureOperation());.
Since a Future is an object, you can assign Future to Object and to anything you can assign Object to, including void. That also means that an async function can be cast to a function type with a return type of void, like:
void Function() f = () async { .... };
You can call that and ignore the result.
And that too is why you can declare the return type of an async method to be void or dynamic or Object?. Because the type system has to allow it anyway.
It would have been different if Future was not a subtype of Object, but that would also make a lot of things more complicated.
It can be used deliberately. If you actually do want a function which does asynchronous operations in the background, but you don't want anyone to wait for them, you can do so:
void log(String message) async {
(await getLogger()).log(message);
}
This function will (eventually) log the message, but you don't have to wait for it to do so.
You could write essentially the same function without using async too, so forcing disallowing a void return type with async would just be an unnecessary obstacle, forcing you to rewrite the same thing in a less readable way.
void log(String message) {
getLogger().then((logger) {
logger.log(message);
});
}
(It's not exactly the same because the async version currently does return a future, it just tells you not to use it but typing it as void. You can, even if you really shouldn't, cast the void return to dynamic and await it anyway. The non-async version doesn't return a future at all.)
I'm a newbie in node.js(firebase functions) and Dialogflow fulfillment, I want to retrieve data in a different directory. first is to check the nearest store, and then check the inventory of the store in a different directory, but I have a problem with return. so how I can fix it?
app.intent('location_checking - yes',(conv)=> {
var store= database.ref('store');
var inventory = database.ref('inventory);
var keystore=[];
return store.orderByKey().on("child_added", function(snapshot){
keystore.push(snapshot.key)
})
return inventory.child(keystore).on("value", function(snapshot){
var tomato =snapshot.val().tomato;
//and then check the nearest store with available stock
})
})
You have a few issues, some of them conceptual.
The first is that you're using on() and the "child_added" event. But since this is happening inside an Intent Handler, which only gets triggered when the user has done something, you can't also listen to conventional events and react to them. Instead, you should probably use once() with a "value" event - so you query for the values you want and work with them.
Second is that the Intent Handler expects you to return a Promise if you are doing any asynchronous operations - anything that would require a callback handler, for example. This would require some restructuring in how you make the calls to once(), so they return a Promise, and you take action in a .then() block. Since you can chain Promises together using .then(), you can make multiple calls this way.
I'm not sure that ordering by key will get you the "closest" store, but I'll ignore that for the moment to illustrate the rest of the code.
So that part of your code might look something like
return store.orderByKey().once("value")
.then( snapshot => {
// Handle the results
// ...
// Make the next query
return inventory.child( childKey ).once("value");
})
.then( snapshot => {
// Handle the inventory results
});
You can also do this with async/await by making your Intent Handler an async function and then calling await on the database calls. Possibly something like.
app.intent('location_checking - yes', async (conv) => {
const store= database.ref('store');
const inventory = database.ref('inventory);
//...
const storeSnapshot = await store.orderByKey().once("value");
// Do something with the store snapshot
const inventorySnapshot = await inventory.child( childKey ).once("value");
// Do stuff with the inventory snapshot
})
The following example is somewhat contrived but I'm trying understand the relationship between a Completer and the future generated by an await.
In the example below the 'doCalc' method contains an 'await' call and a Completer.
Its my understanding that when dart hits the 'await' statement that it immediately stops executing the method and returns a future.
If you run the code in dartpad you can see this in the output. The value = Instance of '_Future<int>' line is printed immediately.
This matches my expectation (i.e. await cause the method to return immediately).
You can see from the 'value=' line that doCalc has returned a Future<int>.
This is also as expected.
The problem I'm having is that the last line of doCalc also returns a future. In this case the future generated by the completer.
As such it appears that doCalc returns TWO different futures.
1) The first one dart implicitly returns when await is called.
2) The second one I explicitly return via the return statement.
Given that the value of 'res' must contain the first future (as we print its value before the completer's future is returned) how is it that the line res.then waits until the second future completes.
There appears to be some magic here but I can't find any documentation on it.
Dart pad link:
https://dartpad.dev/a42cce3edf01b222206a627e8c8106af
import 'dart:async';
void main() {
print('hi');
var res = doCalc();
print('value = ${res}');
res.then((result) => print('result = $result'));
}
Future<int> doCalc() async
{
var done = Completer<int>();
await Future.delayed(Duration(seconds: 4),
() {
done.complete(10);
});
return done.future;
}
An async function returns a future that it creates itself. It does so "immediately", which means that it executes the body until the first asynchronous delay (await or await for) before the future is returned.
Any return statement inside an async function is used to complete the returned future.
If your async function returns a Future<int>, then your return statements must return either an int or a Future<int>.
In the former case, the returned future is completed with that integer.
In the latter case, the returned future will complete with the same result as the return-future (let's name it that) when the return-future completes.
It is as if a return expression; in an async function is effectively a return await expression;.
In this example, the line:
return done.future;
can be read as
return await done.future;
and that should explain the behavior you see.
Dart offers a FutureOr class, that allows writing:
FutureOr<int> future;
future = 42; // valid
future = Future.value(42); // also valid
I would assume that FutureOr would be useful to remove the unnecessary delay caused by the event loop if the value can be read synchronously.
But that doesn't seem to be the case, as showcased by:
import 'dart:async';
void main() async {
print('START');
futureOrExample();
print('END');
}
void futureOrExample() async {
FutureOr<int> futureOr = 42;
print('before await');
await futureOr;
print('end await');
}
which prints:
START
before await
END
end await
when I would expect:
START
before await
end await
END
In that case, why does FutureOr (or more generally await 42) work this way?
Similarly, what's the purpose of FutureOr in that situation since it produces the same result as Future?
I know that I could use SynchronousFuture to achieve the desired result, but I'm just trying to understand what's the use of FutureOr.
The use of FutureOr, as introduced with Dart 2, is to allow you to provide either a value or a future at a point where the existing Dart 1 API allowed the same thing for convenience, only in a way that can be statically typed.
The canonical example is Future.then. The signature on Future<T> is Future<R> then<R>(FutureOr<R> action(T value), {Function onError}).
The idea is that you can have an action on the future's value which is either synchronous or asynchronous. Originally there was a then function which took a synchronous callback and a chain function which took an asynchronous callback, but that was highly annoying to work with, and in good Dart 1 style, the API was reduced to one then method which took a function returning dynamic, and then it checked whether it was a future or not.
In Dart 1 it was easy to allow you to return either a value or a future. Dart 2 was not as lenient, so the FutureOr type was introduced to allow the existing API to keep working. If we had written the API from scratch, we'd probably have done something else, but migrating the existing asynchronous code base to something completely different was not an option, so the FutureOr type was introduced as a type-level hack.
The await operation was also originally defined to work on any object, long before FutureOr existed. For consistency and smaller code, an await e where e evaluated to a non-future would wrap that value in a future and await that. It means that there is only one quick and reusable check on a value (is it a future, if not wrap it), and then the remaining code is the same. There is only one code-path.
If the await worked synchronously on non-Future values, there would have to be a synchronous code path running through the await, as well as an asynchronous path waiting for a future. That would potentially double the code size, for example when compiling to JavaScript (or worse, if there were more awaits in the same control flow, you could get exponential blow-up for a naive implementation). Even if you avoided that by just calling the continuation function synchronously, it would likely be confusing to some readers that an await would not introduce an asynchronous gap. A mistake around that can cause race conditions or things happening in the wrong order.
So, the original design, predating FutureOr, was to make all await operations actually wait.
The introduction of FutureOr did not change this reasoning, and even if it did, it would now be a breaking change to not wait in places where people expect their code to actually give time for other microtasks to run.
The await keyword always lock the function execution.
Writing:
await 42
Is equivalent to:
await Future.value(42)
The reason being:
This is how await works in Javascript
it makes the behavior of await consistent.
Now, what's the purpose of FutureOr then?
FutureOr was never intended as a way to potentially make await synchronous.
Instead, it is an implementation detail of Future.
Without FutureOr, writing the following would not compile:
Future(() {
return 42; // compile error, not a Future
});
Future<int> future;
future.then((value) {
return value * 2; // compile error, not a Future
});
Instead, we would have to wrap all values in a Future.value like so:
Future<int> future;
future.then((value) {
return Future.value(value * 2);
});
for those who are still confused
I found good explanation https://itnext.io/what-is-futureor-in-dart-flutter-681091162c57 without diving into details
This piece of code could explain the target and real use cases of FutureOr
abstract class IDBService {
FutureOr<String> fetch();
}
class FirebaseRemoteService extends IDBService {
#override
Future<String> fetch() async => await 'data';
}
class LocalHiveDbService extends IDBService {
#override
String fetch() => 'data';
}
so in implementations of IDBService
the return type can be Future or String at the same time now!
Coming late to the discussion.
Updating my Dart comprehension - pardon my C++/JS -ish approach.
Seems like this would be useful for singleton initiation. Consider following:
import 'dart:async';
class AClass {
static String _info = '';
static FutureOr<String> get info async {
if (_info.isEmpty) {
print('--> is empty...');
_info = await Future.delayed(Duration(seconds:2),
() => "I'm alive!!");
}
else {
print('--> not empty');
}
return _info;
}
}
Future<void> main() async {
String info = await AClass.info;
print('Fist call: ' + info);
info = await AClass.info;
print('Second call: ' + info);
}
It works as expected - in either case, whether the _info member has been instantiated or not, the getter returns a valid string.
It works fine if I just use a Future<String> specifier in the getter, too. The current implementation makes FutureOr seem mostly like a self-documentation exercise (can return a Future<String> or a String...)
But, even if await currently always locks the execution, a future update may allow it to work as expected, in which case using the FutureOr construct would anticipate updates.
(Aside: I imagine this example could be condensed using an Optional wrapping the _info member, but that's a different exercise...)
I needed to use FutureOr today. I wanted to call a function that might be asynchronously (not always).
String callbackOne() => "hello";
Future<String> callbackTwo() async => (await Future.delayed(Duration(seconds: 1),() => "This is a sentence"));
Problem
I can do getLengthOfResult(callbackOne), but not getLengthOfResult(callbackTwo). Conversely, if accept an async callback, I can't use the sync callback.
DartPad
Future<int> getLengthOfResult(String Function() callback) async {
return callback().length;
}
Solution
DartPad
Future<int> getLengthOfResult(FutureOr<String> Function() callback) async {
// I can await on callbackOne, even though it returns a String.
final result = await callback();
return result.length;
}
main() {
getLengthOfResult(callbackOne);
getLengthOfResult(callbackTwo);
}