Future<void> async vs simply void async in Dart
Asked Answered
U

2

11

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?

Uturn answered 24/2, 2021 at 2:55 Comment(6)
I'm pretty sure this is an identical question that I did actually answer: What's the difference between returning void vs returning Future<void>?Medley
Well I didn't know that question was there, thx... it's not the greatest though as the actual difference is hashed out in comments and seems inconclusive.Uturn
You cant await the void, you can await the Future<void> that's the difference.Jacques
@NerdyBunz Huh? The difference is explained in the answer: callers cannot wait for an asynchronous void function to complete. Period. There's nothing inconclusive about that.Medley
I predict that comment will be deleted. :DUturn
I don't know what more you want, I answered it. They are both async, one can be awaited, the other can not. That's the difference. The fact you could await void previously was a bug, and has been fixed.Jacques
M
10

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.

Medley answered 26/2, 2021 at 7:26 Comment(0)
C
3

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.

Chemosmosis answered 24/2, 2021 at 3:10 Comment(2)
Future<void> does not requre a return statement AFAIK. The difference is just that one can be awaited and the other can not I think? I actually didn't realize that would compile...Jacques
@Jacques Yeah currently it's not required, only a warning. You're right as well, the void function cannot be awaited since it indicates it's not gonna return anythingChemosmosis

© 2022 - 2024 — McMap. All rights reserved.