Dart Future tutorial with examples

Tien Nguyen - Oct 28 '22 - - Dev Community

Asynchronous programming allows a program to do work while waiting for something else to complete. In this tutorial, we'll show you how to work with Future in Dart (also in Flutter). At the end, you're gonna know:

  • Introduction to Dart Future
  • How to create Future, Future delayed and value methods
  • Basic Future usage: then, catchError and callback
  • Way to use Future async-await
  • Future multiple await/then for chain of multiple asynchronous methods
  • Equivalent of 'finally': Future whenComplete
  • Future wait for multiple futures complete

This tutorial is originally from Bezkoder:
https://www.bezkoder.com/dart-future/

Dart Future overview

A Future represents a potential value (success), or error (fail), that will be available in the future. Generally we will need to work with a future value in following cases:

  • getting data from a server
  • querying data from a database
  • reading a file
  • doing a complicated computational work
Future<int> future = getFuture();
Future<Tutorial> tutorialsFut = getTutorialsFromDatabase(query);
Future<Data> dataFut = fetchDataFromServer(url);
Future<int> resultFut = computeBigData(value);
Enter fullscreen mode Exit fullscreen mode

Dart Future then

We can use then() to indicate code that runs when the future completes.

For example, fetchDataFromServer() returns a Future.

Future<Data> future = fetchDataFromServer(url);
future.then((Data data) {
  print(data.values);
});
Enter fullscreen mode Exit fullscreen mode

Dart Future callback

In the example above, we register a callback that handles the value of a Future result. How about an error? We're gonna use catchError(callback).

For example:

Future<Data> future = fetchDataFromServer(url);
future.then((data) => handleData(data))
      .catchError((error) => handleError(error));
Enter fullscreen mode Exit fullscreen mode

Dart async-await

The async and await keywords provide a declarative way to define asynchronous function (that returns a Future) and use its result.

We must remember 2 basic conditions:

  • Asynchronous function has async before the function body.
  • The await keyword works only in async functions.

For example, we're gonna re-write the Future then above with async-await style.

main() async {
  Data data = await fetchDataFromServer(url);
  handleData(data);
}
Enter fullscreen mode Exit fullscreen mode

Furthermore, then-catchError is equivalent to async-await like this:

main() async {
  try {
    Data data = await fetchDataFromServer(url);
    handleData(data);
  } catch (error) {
    handleError(error);
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's continue with some simple ways to create a Future.

Dart Future delayed

Future<T>.delayed(Duration duration, [computation()?])
Enter fullscreen mode Exit fullscreen mode

delayed() method creates a Future that runs computation after the given duration has passed, and the future is completed with the result of the computation.

If the duration is 0 or less, it completes no sooner than in the next event-loop iteration, after all microtasks have run.

For example:

Future.delayed(Duration(seconds: 2), () => 'Delayed 2 seconds.')
      .then((result) => print(result));
Enter fullscreen mode Exit fullscreen mode

async-await:

var result = await Future.delayed(Duration(seconds: 2), () => 'Delayed 2 seconds.');
print(result);
Enter fullscreen mode Exit fullscreen mode

Output:

(after 2 seconds)
Delayed 2 seconds.

Dart Future value

Future<T>.value(value)
Enter fullscreen mode Exit fullscreen mode

The method Future.value() creates a Future completed with value.

For example:

Future.value("bezkoder.com")
      .then((result) => print(result));
Enter fullscreen mode Exit fullscreen mode

async-await:

var result = await Future.value("bezkoder.com");
print(result);
Enter fullscreen mode Exit fullscreen mode

Output:

bezkoder.com

Dart Future error

Future.error(Object error) creates a future that completes with an error.

For example:

Future.error('> This is an error message!')
      .catchError((err) => print(err));
Enter fullscreen mode Exit fullscreen mode

async-await:

try {
  await Future.error('> This is an error message!');
} catch (err) {
  print(err);
}
Enter fullscreen mode Exit fullscreen mode

Output:

> This is an error message!

Write asynchronous Function with Future

Now we create a asynchronous function that returns a Future. The function receives an input int number. If the input number is less than 0, it completes with a Future error, or else, completes after given time and return the input number.

Future<int> doInSeconds(int sec) {
  print('> Processing with value=${sec}...');

  if (sec <= 0) {
    return Future.error('> input \'sec\' must be greater than 0');
  }

  return Future.delayed(Duration(seconds: sec), () {
    print('> Something done in ${sec} second(s).');
    return sec;
  });
}
Enter fullscreen mode Exit fullscreen mode

Let's use doInSeconds() function.

doInSeconds(2).then((result) {
  print(result);
}).catchError((error) {
  print('> inside catchError');
  print(error);
});
Enter fullscreen mode Exit fullscreen mode

Or:

try {
  var result = await doInSeconds(2);
  print(result);
} catch (error) {
  print(error);
}
Enter fullscreen mode Exit fullscreen mode

Output:

> Processing with value=2...
(after 2 seconds)
> Something done in 2 second(s).
2
Enter fullscreen mode Exit fullscreen mode

Check the catching Error:

try {
  var result = await doInSeconds(-1);
  print(result);
} catch (error) {
  print('> inside catchError');
  print(error);
}
Enter fullscreen mode Exit fullscreen mode

Output:

> Processing with value=-1...
> inside catchError
> input 'sec' must be greater than 0

Chain of multiple asynchronous methods in Dart

This is how we handle the case in that we need to call run multiple asynchronous functions in a certain order.

Future future = doInSeconds(1);
future
    .then((v1) => doInSeconds(v1 + 1))
    .then((v2) => doInSeconds(v2 + 1))
    .catchError((error) => print(error));
Enter fullscreen mode Exit fullscreen mode

Output:

> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=2...
> Something done in 2 second(s).
> Processing with value=3...
> Something done in 3 second(s).
  • If the callback inside then() returns a Future, then() returns a Future that will complete with the same result.
  • If the callback returns a value of any other type, then() creates a new Future that completes with the value.

Dart multiple await

Let's run multiple asynchronous functions in chain using async-await:

try {
  final v1 = await doInSeconds(1);
  final v2 = await doInSeconds(v1 + 1);
  await doInSeconds(v2 + 1);
} catch (error) {
  print(error);
}
Enter fullscreen mode Exit fullscreen mode

Output:

> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=2...
> Something done in 2 second(s).
> Processing with value=3...
> Something done in 3 second(s).

Dart Future whenComplete

We've known that then-catchError is equivalent to a try-catch, so whenComplete is for finally.

The callback inside whenComplete() is called after all, whether the result is with a value or with an error.

try-catch-finally:

try {
  final v1 = await doInSeconds(1);
  final v2 = await doInSeconds(v1 + 1);
  await doInSeconds(v2 + 1);
} catch (error) {
  print(error);
} finally {
  print("Done!");
}
Enter fullscreen mode Exit fullscreen mode

equivalent:

Future future = doInSeconds(1);
future
    .then((v1) => doInSeconds(v1 + 1))
    .then((v2) => doInSeconds(v2 + 1))
    .catchError((error) => print(error))
    .whenComplete(() => print("Done!"));
Enter fullscreen mode Exit fullscreen mode

Output:

> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=2...
> Something done in 2 second(s).
> Processing with value=3...
> Something done in 3 second(s).
Done!

Check another code that will cause an error.

Future future = doInSeconds(1);
future
    .then((v1) => doInSeconds(v1 - 1))
    .then((v2) => doInSeconds(v2 - 1))
    .catchError((error) => print(error))
    .whenComplete(() => print("Done!"));
Enter fullscreen mode Exit fullscreen mode

Output:

> Processing with value=1...
> Something done in 1 second(s).
> Processing with value=0...
> input 'sec' must be greater than 0
Done!

Future wait in Dart

If you need to run many asynchronous functions and wait for them all to complete before continuing something else, you can use static method: Future.wait() to manage multiple Futures:

var query = doInSeconds;
var compute = doInSeconds;
var work = doInSeconds;
await Future.wait([
  query(1),
  compute(6),
  work(3),
]);
print('Done!');
Enter fullscreen mode Exit fullscreen mode

Output:

> Processing with value=1...
> Processing with value=6...
> Processing with value=3...
> Something done in 1 second(s).
> Something done in 3 second(s).
> Something done in 6 second(s).
Done!

Conclusion

In this tutorial, we've learned overview of a Dart/Flutter Future, how to create a Future simply using delayed or value method, how to work with then-catchError-whenComplete or try-catch-finally with Future async-await, way to handle chain of multiple asynchronous methods or Future wait for multiple futures complete.

Happy learning! See you again!

Further Reading

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .