Reputation: 96556
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
Upvotes: 10
Views: 10673
Reputation: 19
I think Future should be forced.
you can add avoid_void_async in your lint file: https://dart-lang.github.io/linter/lints/avoid_void_async.html
Upvotes: 1
Reputation: 96556
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:
void
, it does not return a value (and using the result of the expression is a compiler error).async
:
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
.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
).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
Upvotes: 3
Reputation: 544
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.
Upvotes: 0
Reputation: 2688
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.
Upvotes: 6