Reputation: 7497
In Dart,
what is the difference between saying
Future<void> doStuff() async { ...
and
void doStuff() async { ...
I know what a Future<T> is and how async/await work generally, but I never realized Future<void> was a thing. I have some code that has the Future<void> all over the place and I want to replace it with my normal way of doing things, but I don't want to break anything.
Notice that both functions use async. The question is NOT 'what is the difference between async and non-async functions?' or 'can you give a brief attempt at explaining asynchronous programming in Dart, please?'
I'm aware that there is a pretty much identical question already, but if you look closely at the answers you will see nobody actually answered the question in a clear way -- what is the difference? Is there any difference? Is there no difference?
To elaborate, consider the following two functions:
// notice there is no warning about not returning anything
Future<void> futureVoid() async {
await Future.delayed(Duration(seconds: 2), () {
var time = DateTime.now().toString();
print('$time : delay elapsed');
});
}
void nonFutureVoid() async {
await Future.delayed(Duration(seconds: 2), () {
var time = DateTime.now().toString();
print('$time : delay elapsed');
});
}
Then test them with a button whose onPressed() function is:
onPressed: () async {
await nonFutureVoid(); // notce that this await *DOES* delay execution of the proceeding lines.
var time = DateTime.now().toString();
print('$time : <-- executed after await statement');
}
Log result:
flutter: 2021-02-23 21:46:07.436496 : delay elapsed
flutter: 2021-02-23 21:46:07.437278 : <-- executed after await statement
As you can see, they both behave exactly the same way -- the simple void async version IS awaited. So what is the difference?
Upvotes: 11
Views: 3825
Reputation: 90174
With your edit, your question makes more sense. Your question is really about principle vs. practice.
In principle, a function that returns void
is different from a function that returns Future<void>
. One conceptually represents something that does not return anything, and the other represents an asynchronous computation that can fire callbacks when it completes.
In principle, you should never attempt to use the value returned from a void
function. It doesn't make sense. If you run the Dart analyzer against code that does await nonFutureVoid();
you'll get a warning if the await_only_futures
lint is enabled.
In practice, there are cases where attempting to use a void
return value happens to not generate an error. Those are quirks in the language (or bugs in the implementation); you should not rely on them. (Dart didn't originally have a void
return type. When it was added later, it wasn't implemented to mean "no value" but instead to mean "a value that you aren't allowed to use". See Dart 2: Legacy of the void
. Normally that subtle difference shouldn't matter.)
Being able to do await nonFutureVoid();
is a bug1, and it seems that that bug is now fixed: await nonFutureVoid();
is an error if you use Dart 2.12 or later and enable null-safety and the stricter type-checking that comes with it.
You can observe the old and new behaviors with DartPad by toggling the "Null Safety" button: https://dartpad.dartlang.org/b0dae0d5a50e302d26d93f2db3fa6207
1 There are a lot of issues filed on GitHub with a lot of back-and-forth discussion, so yes, it is rather confusing. Most people seemed to agree that allowing await void
was undesirable, however.
Upvotes: 10
Reputation: 3326
A void
function indicates that the function returns nothing, which means you can not act on the result of that Function (and cannot await
on it's result).
Meanwhile, the Future<void>
function returns a Future object (that has value of void
). If you don't have a return
statement, a missing_return
warning will show up (it can still be compiled). You can still act on that result by awaiting it, but cannot actually use the value because it's void
.
While it seems like it'd be just fine with whatever you are using, I think it's better to use Future
for every async
function for type-safety and better maintenance.
Upvotes: 3