Timmmm
Timmmm

Reputation: 96556

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

Upvotes: 10

Views: 10673

Answers (4)

王楠楠
王楠楠

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

Timmmm
Timmmm

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:

  1. 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.

  2. 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

Upvotes: 3

AKushWarrior
AKushWarrior

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

FutureJJ
FutureJJ

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

Related Questions