What the consequence does await introduce the program in dart? - 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.

Related

FutureOr can await void functions?

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.

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>

Async await Future.delayed in dart

I am having trouble understanding why one piece of code prints the future is null
void main() async {
task1();
String str = await task2();
task3(str);
}
void task1() {
print('ring');
}
Future<String> task2() async {
Duration dur = Duration(seconds: 3);
String res;
await Future.delayed(dur, () {
res = 'a bright one!';
return res; // return statement inside the callback.
});
}
void task3(String str) {
print('the future is $str');
}
while this works properly and has the expected behavior of printing 'the future is a bright one'
void main() async {
task1();
String str = await task2();
task3(str);
}
void task1() {
print('ring');
}
Future<String> task2() async {
Duration dur = Duration(seconds: 3);
String res;
await Future.delayed(dur, () {
res = 'a bright one!';
});
return res;
}
void task3(String str) {
print('the future is $str');
}
I am new to asynchronous programming but my understanding is that the callback that is the second argument in Future.delayed is executed after a delay what I don't understand why the placement of the return statement here breaks the code. I tried to run the code in debug mode to trace the code but I didn't understand what is exactly happening. All help is greatly appreciated

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.

The Fat Arrow is syntactic sugar - but do not return

The Fat Arrow is syntactic sugar, like described here
But if I remove return in(look for: future.then(print); // << Removed return) then it complains about missing return?:
Hmm, or am I missing a "return-return" kind og thing...
import 'dart:async';
main() {
printsome();
print("After should be called first...");
}
Future<void> printsome() {
final future = delayedFunction();
future.then(print); // << Removed return
// You don't *have* to return the future here.
// But if you don't, callers can't await it.
}
const news = '<gathered news goes here>';
const oneSecond = Duration(seconds: 1);
Future<String> delayedFunction() =>
Future.delayed(oneSecond, () => news);
I get this warning:
[dart] This function has a return type of 'Future<void>', but doesn't end with a return statement. [missing_return]
The => expr implicitly returns the result of expr, so if you want to replace it with a function body { return expr; } you need to add return, otherwise null will be implicitly returned.
You declared a function with return type Future<void> and DartAnalyzer warns you that your function is not returning such a value.
You can either add async which makes the function implicitly return a Future
Future<void> printsome() async {
final result = await delayedFunction();
}
or if you don't want to use async you can just add return
Future<void> printsome() {
final future = delayedFunction();
return future.then(print); // return the Future returned by then()
}

Resources