FutureOr can await void functions? - dart

As far as I understand it should not be possible to await functions like void foo() async {}, because they return no future that could be awaited. But I noticed when I assign them to a FutureOr<void> Function() field it is possible to await them. The following example prints: PRE, FUNC, POST
import 'dart:async';
void func() async {
await Future.delayed(const Duration(seconds: 2));
print('FUNC');
}
Future<void> main() async {
final FutureOr<void> Function() t = func;
print('PRE');
await t();
print('POST');
}
I would expect it to print: PRE, POST, FUNC
Because the function func cannot be await (because it returns void) and therefore the await t() should not wait for the completion of the func function.
I observed the same behavior with final dynamic Function() t = func;
I'm not sure if this behavior is intended or if I just don't understand void async functions?

The await operator can be used on any object and on an expression of any type except void (and that's only because the type void is specifically disallowed in most contexts, not something special for await).
An async function always returns a future. Even if its declared return type is void. Declaring the return type as void is just a signal to users that they shouldn't expect a useful value, and it makes it harder to actually get to the value (because of the above-mentioned restrictions on using void typed expressions).
In fact, all void return-type functions return a value (unless they throw). That value is usually null, but it's not required to be.
Since you can override a void foo() method with int foo() in a subclass, and assign an int Function() to a variable of type void Function(), the compiler can't actually know for sure that a function with static type void does not return anything. So, it doesn't even try to enforce it for functions that are themselves typed as returning void.
The type FutureOr<void> is a supertype of void (and of Future<void>), so a void Function() function object is assignable to the type FutureOr<void> Function().
All in all, that conspires to allow your code to work the way you see.
The await t() performs an await on an expression with static type FutureOr<void> and an actual value which is a Future<void>, which was returned by a call to func. So, the await t() waits for FUNC to be printed.

Don't use await if you want skip task. Use Future.sync() or Future.microtask() in you case. Documentation
import 'dart:async';
void func() async {
await Future.delayed(const Duration(seconds: 2));
print('FUNC');
}
Future<void> main() async {
final FutureOr<void> Function() t = func;
print('PRE');
Future.microtask(t);
print('POST');
}
By using await you telling to compilator that you want to wait until some task will be completed and no mater it's void or not.

Related

Break async / wait declaration chain in Dart

I wonder if you can escape the async / wait chain in Dart. AFAIK every time you want to make a sync call to an async function you must use await but this forces you to make the function which holds the instruction to become async too.
Here is an example:
Future<String> helloAsync() async{
return Future.delayed(const Duration(seconds: 2), ()=>'Hello');
}
void helloSync(){
//call helloAsync synchronously (somehow without making this function async)
//print returned value
}
void main(){
helloSync();
}
In here if I want to make a sync call to helloAsync() inside the HellowSync() I must add await like:
void helloSync(){
String s = await helloAsync();
print(s);
}
but this will not work until I add the async keyword for it - and if I do this means that I have to do it for main() function too.
Is there a way to break this chain of async / await declaration?
(In the initial example this would mean no async declaration for main function)
You could use .then() instead. You can't return the result of a Future from a synchronous function, but since your helloSync() returns void anyway, .then() might be what you are looking for.
void helloSync(){
helloAsync().then((result) => print(result));
}

A value of type 'Future<bool>' can't be returned from the function because it has a return type of 'Future<void>'

I have:
Future<bool> foo() async => true;
This is allowed:
Future<void> bar() {
return foo(); // Works
}
but this is not:
Future<void> baz() async {
return foo(); // Error
}
In both bar and baz I'm returning a Future<bool>, but why first works but second fails?
Note: This question isn't about HOW to make it work but rather WHY one works and the other doesn't.
Dart has special rules for void returning functions: You are not allowed to return a value because it's declared as not returning any value, so actually trying to return a value is probably a mistake.
The same happens without Future:
void qux() {
return true; // Error, can't return value from void function
}
gives the error
A value of type 'bool' can't be returned from the function 'qux' because it has a return type of 'void'.
You are allowed to have return e; statements, but only if the type of e is void, Null or dynamic. (And you shouldn't even do that, those are just allowed in order to make existing code work.)
(A => e body is always allowed, because people like to use it as a shorthand for just { e; }, mainly because the formatter keeps it on one line. I still recommend using { e; } as body of a void function.)
Generalizing that to async functions with void future return types,
you are not allowed to return an actual value from Future<void> ... async function. So, the only things you are allowed to return are void, dynamic, Null, Future<void>, Future<dynamic>, and Future<Null>.
A Future<bool> is neither of those.
What you should write instead is:
Future<void> baz() async {
await foo();
}
As I have observed when I am running the same code
Future<bool> foo() async => true;
Future<void> bar() {
return foo(); // Works
}
Future<void> baz() async {
// return foo(); // Error
}
void main() {
print("direct =====${foo()}");
print("bar==========${bar()}");
print("bazz========${baz()}");
}
The response is
direct =====Instance of '_Future<bool>'
bar==========Instance of '_Future<bool>'
bazz========Instance of '_Future<void>'
this signifies that when the async keyword is not present it takes the return type of the returning value.
But of the bazz function it gives a quick fix to make the function
Future<Future<bool>>
so when async is added the function return type is taken as the main type
Future<bool> foo() async => true;
First Case
Future<void> bar() {
return foo(); // Works
}
For the code above, the return of Future<void> bar() can return foo(), because the bar() not return directly the value of foo(), so the bar() can deal with foo() without no problem.
But, in the second case:
Future<void> baz() async {
return foo(); // Error
}
The code above is return directly the generic type of foo() because the function of baz() include async tag, so it will be trouble with the baz() as Future<void>, which is we know that foo() as Future<bool>

What the consequence does await introduce the program in dart?

Why is the follow code valid? I thought there should be compile-time errors. I thought the return in the body return an int(value 1). If not, it must be returning a Future, which does not comply to the return type either. What has happened with the await?
void longRun() async {
return await Future.delayed(
Duration(seconds: 3),
()=>1,
);
}
If I assign the await part to a variable, like the following, the compiler starts to complain, which is obvious and easy to understand. But why doesn't the code above complain?
void longRun() async {
var foo = await Future.delayed(
Duration(seconds: 3),
()=>1,
);
return foo;
}
Keep studying I've found more confusing thing: all the following versions work. They seem identical, wired.
version1:
void longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
version2:
Future<void> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
version3:
Future<int> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
version4:
Future longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
version5:
Future longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
version6:
void longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
This is mostly about the behavior of =>, not about await. Normally () => 1 can be thought of as shorthand for () { return 1; }, but that's actually an oversimplification.
=> is allowed for void functions as a convenience so that people can write things like:
bool functionWithSideEffect() {
print('Some side effect');
return true;
}
void foo() => functionWithSideEffect();
int _someValue = 0;
set someValue(int value) => _someValue = value;
even though these aren't legal:
var foo() {
// error: A value of type 'bool' can't be returned from the function 'foo'
// because it has a return type of 'void'.
return functionWithSideEffect();
}
set someValue(int value) {
// error: A value of type 'int' can't be returned from the function
// 'someValue' because it has a return type of 'void'.
return _someValue = value;
}
The crux of your confusion is that () => 1 either could be int Function() or void Function(), and type inference picks different things given different constraints.
When you do:
void longRun() async {
return await Future.delayed(
Duration(seconds: 3),
()=>1,
);
}
then type inference works outside-in, propagating the void return type of longRun through the unconstrained, returned expression. The Future.delayed call is inferred to be Future<void>.delayed and the () => 1 callback is inferred to be void Function(). (Arguably a void async function returning Future<void> could be treated as error.)
In contrast, when you do:
void longRun() async {
var foo = await Future.delayed(
Duration(seconds: 3),
()=>1,
);
return foo;
}
now type inference runs in the other direction (inside-out): foo has no explicit type, so it does not constrain the right-hand-side. Therefore () => 1 is instead assumed to be int Function(), which causes Future.delayed to be inferred to be Future<int>.delayed and foo to be inferred as an int. Since foo is an int, attempting to return it from a function declared to return void is an error.
version2:
Future<void> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
You've changed longRun's return type from void to Future<void>. async functions usually should return a Future but may also return void to be fire-and-forget. The only difference from version 1 is that callers can now wait (fire a callback) when longRun completes. () => 1 is still inferred to be void Function().
version3:
Future<int> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
Same as version2 except longRun returns Future<int> instead of Future<void>. Now () => 1 is inferred to be int Function().
version4:
Future longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
longRun's return type is now Future, which means Future<dynamic>. () => 1 is inferred to be dynamic Function().
version5:
Future longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
Same as version4 except there is no explicit return statement. Now the Future.delayed expression is no longer constrained by longRun's return type, and inference will flow inside-out; () => 1 is assumed to be int Function().
version6:
void longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
Same as version5 except that longRun returns void and is a fire-and-forget function. Callers cannot be notified when longRun completes.
(Note that in the above, when I write () => 1 is inferred to be int Function() or void Function(), it really should be FutureOr<int> Function() or FutureOr<void> Function(), but that's not the important part.)
Edit:
Pre-emptively addressing some potential follow-up questions:
Why is:
void longRun() async {
int f() {
return 1;
}
return await Future<void>.delayed(
Duration(seconds: 3),
f,
);
}
okay, but:
void longRun() async {
return await Future<void>.delayed(
Duration(seconds: 3),
// info: Don't assign to void.
() {
return 1;
},
);
}
generates an analysis complaint? Both versions are legal because substituting a Future<U> for a Future<T> is legal when U is a subtype of T. When T is void, U can be anything; the value can just be ignored. (In Dart, partly for historical reasons when it didn't always have a void type, void actually means that the value cannot be used and not that there is no value. void x = 42; is legal, but attempting to read the value of x would be an error. This is why the "Don't assign to void" analysis complaint isn't categorized as an error.)
In the second version that uses an anonymous function, the tighter locality of the return statement allows the analyzer to perform more extensive checks.

Return type of void async function in Dart

In Dart, if you have an async function that doesn't return anything, should it return Future<void> or simply void? Both seem to work, but why?
void foo() async {
print('foo');
}
Future<void> bar() async {
print('bar');
}
void main() async {
await foo();
await bar();
print('baz');
}
Compiles with no errors or warnings and prints
foo
bar
baz
In your code, both functions foo() and bar() are not returning anything so any function return type is valid for both functions eg:
void/T/Future<T>... foo() async {
print('foo');
}
Future<void>/T/Future<T>... bar() async {
print('bar');
}
and here,
await foo();
await bar();
await just waits till the execution of these async functions is complete. As there is no return type, await has no purpose here(redundant) so this is and should not be a compilation error.
The difference is that Future<void> gives you information of the execution of the function like when the execution is complete and it also allows you to specify what to do when the function is executed by using bar().then(...) and bar().whenComplete(...).
While foo() function return void and void does not hold as such any info like Future<void>. If you try to await bar() it will convert that Future object from Future<void> to void.
Future is just a container, with await returns the values once the async "tasks" are completed. Without await, Future objects give you information about the execution of the function from which they are returning.
Thanks to the other answers - they were a little unclear to me so I'm just going to add some clarifications after experimenting on DartPad:
It is never an error or even a warning! to not return a value from a function, irrespective of the return type. This madness is presumably inherited from Javascript.
If you don't return from a function it implicitly returns null. Except in these cases:
If the return type is void, it does not return a value (and using the result of the expression is a compiler error).
If the function is async:
If the return type is void, you cannot use the result of the function, however you can still await it but you cannot use the result of the await expression, or use any methods of Future.
If the return type is Future<void> it returns an instance of Future<void>. You can await it, and call methods of Future, e.g. .then((void v) { ... });. However you cannot use the result of await (because it is void).
If the return type is Future<T> it returns an instance of Future<T> that resolves to null.
So basically, if you want to allow callers to use Future methods, you need to annotate the return type as Future<void>. If you merely want them to be able to await the function, you only need void. However since you probably don't know in advance I suspect it is a good idea to always use Future<void>.
Here's an example that demonstrates the possibilities:
// Compilation error - functions marked async must have a
// return type assignable to 'Future'
// int int_async() async {
// }
void void_async() async {
}
Future<void> future_void_async() async {
}
Future<int> future_int_async() async {
}
int int_sync() {
}
void void_sync() {
}
Future<void> future_void_sync() {
}
Future<int> future_int_sync() {
}
void main() async {
// print('${void_async()}'); // Compilation error - expression has type void.
print('future_void_async: ${future_void_async()}');
print('future_int_async: ${future_int_async()}');
// print('${await future_void_async()}'); // Compilation error.
await future_void_async();
future_void_async().then((void v) {
print('ok');
});
await void_async();
// void_async().then((void v) { // Compilation error.
// print('ok');
// });
print('await future_int_async: ${await future_int_async()}');
print('int_sync: ${int_sync()}');
// print('${void_sync()}'); // Compilation error - expression has type void
print('future_void_sync: ${future_void_sync()}');
print('future_int_sync: ${future_int_sync()}');
}
It prints
future_void_async: Instance of '_Future<void>'
future_int_async: Instance of '_Future<int>'
ok
await future_int_async: null
int_sync: null
future_void_sync: null
future_int_sync: null
A Future is simply a representation of an Object that hasn't completed the underlying function, and thus is a "promise" for later use. When you use Future<void>, there's no Object to return anyways, so it doesn't matter whether you use void or Future<void>. (The function doesn't return anything, so the return type is a placeholder anyways.)
However...
Future<void> can be used for listeners, and to check when things are complete, as can every Future. This means that you can use Future's listeners to check for completion or errors in running your Future<void> async function, but not your void async function. Thus, in some cases, it's better to give Future<void> than void as the return type.

How does onTap VoidCallback handler work when it's an async (returning a Future<Null>)?

I'm trying to push a route to display a ListView item when that item is tapped (onTap). The onTap property for InkWell is defined as a VoidCallback. However, I would like to be able to receive a return value from the callback, indicating whether the user has modified said item in the dialog that was opened.
Assigning a Future<Null> async function to onTap seems to be OK, as per the Shrine Demo (shrine_home.dart in the flutter_gallery example):
Future<Null> _showOrderPage(Product product) async {
final Order order = _shoppingCart[product] ?? new Order(product: product);
final Order completedOrder = await Navigator.push(context, new ShrineOrderRoute(
order: order,
builder: (BuildContext context) {
return new OrderPage(
order: order,
products: _products,
shoppingCart: _shoppingCart,
);
}
));
assert(completedOrder.product != null);
if (completedOrder.quantity == 0)
_shoppingCart.remove(completedOrder.product);
}
Why and how is this working? Thanks.
Dart allows you to use a function with a non-void return value as a function with a void return value. The function will still return values as normal, but the analyzer will infer that they're of the void type and will complain if you try to use them, unless you cast them back to something else.
typedef void VoidCallback();
typedef int IntCallback();
final IntCallback i = () => 42;
void main() {
final VoidCallback v = i;
print("${v()}"); // prints 42
print(v() as int); // also prints 42
print(v().toString()); // also prints 42, but analyzer complains:
// method 'toString' isn't defined for class 'void'
}
That is why you can use a function that returns a Future<Null> as a VoidCallback.
Oops. Just realized the assignment was actually via a lambda, which is a VoidCallback:
onPressed: () { _showOrderPage(product); }
Still - it seems to be working for me in another place, where I assign a Future<Null> async function to the onPressed method of FloatingActionButton...

Resources