Dart Lesson 66 of 102 3 min read
Future in Dart: Working with Values That Arrive Later
Learn the Future class in Dart: states of a future, then, catchError, whenComplete, Future.value, Future.delayed and chaining futures.
On this page
A Future<T> is an object that represents a value of type T that will be available at some point. It is the return type of nearly every asynchronous function in Dart.
The three states #
A future is always in one of these states:
- Uncompleted: the work is still going on.
- Completed with a value: it succeeded and holds the result.
- Completed with an error: it failed and holds an exception.
Once completed, a future never changes again.
Creating futures #
Future<void> main() async {
// Already finished with a value.
var ready = Future.value(42);
// Finishes after a delay.
var later = Future.delayed(Duration(seconds: 1), () => 'done');
// Runs a function a little later and completes with its result.
var computed = Future(() => 2 + 2);
print(await ready);
print(await later);
print(await computed);
}
42
done
4
In practice you rarely create futures yourself. You receive them from libraries: http.get(...), file.readAsString(), database.query(...).
A function that returns a future #
Future<String> fetchUserName(int id) {
return Future.delayed(Duration(seconds: 1), () => 'user_$id');
}
void main() {
var future = fetchUserName(7);
print(future); // not the name, just the pending future
future.then((name) => print('Got $name'));
}
Instance of 'Future<String>'
Got user_7
Printing the future does not give you the name. A very common beginner bug is forgetting to wait for the value.
then: do something with the result #
then registers a function to run when the future succeeds. It returns a new future, so calls can be chained.
Future<int> fetchPrice() => Future.delayed(Duration(milliseconds: 500), () => 100);
void main() {
fetchPrice()
.then((price) => price * 1.13) // add tax
.then((total) => total.toStringAsFixed(2))
.then((text) => print('Total: Rs. $text'));
}
Total: Rs. 113.00
catchError and whenComplete #
Future<String> loadProfile(bool online) {
return Future.delayed(Duration(milliseconds: 300), () {
if (!online) throw Exception('No internet');
return 'Profile loaded';
});
}
void main() {
loadProfile(false)
.then((value) => print(value))
.catchError((error) => print('Failed: $error'))
.whenComplete(() => print('Hide loading spinner'));
}
Failed: Exception: No internet
Hide loading spinner
catchErroris likecatch. It handles a failure from anywhere earlier in the chain.whenCompleteis likefinally. It runs either way.
The same code with async and await #
Everything above is easier to read with async/await, covered in the next lesson. Use that style by default.
Future<String> loadProfile(bool online) {
return Future.delayed(Duration(milliseconds: 300), () {
if (!online) throw Exception('No internet');
return 'Profile loaded';
});
}
Future<void> main() async {
try {
print(await loadProfile(false));
} catch (error) {
print('Failed: $error');
} finally {
print('Hide loading spinner');
}
}
Future<void> #
A function that does asynchronous work but has no result returns Future<void>. The caller can still wait for it to finish.
Future<void> saveSettings() async {
await Future.delayed(Duration(milliseconds: 200));
print('Settings saved');
}
Future<void> main() async {
await saveSettings();
print('Back to the home screen');
}
Settings saved
Back to the home screen
Useful constructors and methods #
| Member | Purpose |
|---|---|
Future.value(x) | A future that is already complete |
Future.error(e) | A future that has already failed |
Future.delayed(d, fn) | Complete after a duration |
Future.wait([...]) | Wait for several futures; see running futures in parallel |
future.timeout(d) | Fail if it takes longer than d |
future.then(fn) | Use the value |
future.catchError(fn) | Handle a failure |
future.whenComplete(fn) | Run clean-up either way |
Try it yourself #
Write Future<int> slowSquare(int n) that waits one second and returns n * n. Call it with .then and print the result. Then make it throw when n is negative and handle that with catchError.