Futures and async/await
Prerequisites: Exceptions and error handling, Anonymous functions and closures
Some work takes time: network requests, reading files, database queries.
Dart runs your code on a single thread, so instead of blocking while it
waits, it uses asynchronous code. A Future represents a value
that will be available later.
Real-world analogy: ordering at a café. You get a receipt (the
Future) straight away, step aside so others can order, and collect
your coffee when your number is called.
async and await
Future<String> fetchUser(int id) async {
await Future.delayed(Duration(milliseconds: 500)); // stands in for a network call
return 'User $id';
}
Future<void> main() async {
print('Fetching...');
final user = await fetchUser(1);
print(user);
}
Fetching...
User 1
asyncmarks a function that returns aFuture. Inside it, you can useawait.awaitpauses the function until the future completes — without blocking the program, which carries on with other work (like keeping a Flutter app responsive).maincan beasynctoo.
Without await, code keeps going
Future<String> fetchUser(int id) async {
await Future.delayed(Duration(milliseconds: 100));
return 'User $id';
}
Future<void> main() async {
final future = fetchUser(1);
print('Request started');
print(await future);
}
Request started
User 1
Calling an async function starts it immediately and returns a future;
the result arrives later.
Running futures concurrently
Each await waits before the next line runs. When calls don't depend on
each other, start them all and wait once:
Future<String> fetchUser(int id) async {
await Future.delayed(Duration(seconds: 1));
return 'User $id';
}
Future<void> main() async {
final stopwatch = Stopwatch()..start();
final (a, b) = await (fetchUser(1), fetchUser(2)).wait;
print('$a, $b');
final all = await Future.wait([fetchUser(3), fetchUser(4)]);
print(all);
print('Took about ${stopwatch.elapsed.inSeconds} seconds');
}
User 1, User 2
[User 3, User 4]
Took about 2 seconds
Each pair takes about one second instead of two. (f1, f2).wait waits
for a record of futures and keeps each result's type; Future.wait
takes a list.
Errors in async code
A failed future throws at the await, so ordinary try/catch works:
Future<String> loadProfile(int id) async {
await Future.delayed(Duration(milliseconds: 100));
if (id <= 0) throw ArgumentError('invalid id: $id');
return 'Profile $id';
}
Future<void> main() async {
try {
print(await loadProfile(7));
print(await loadProfile(0));
} on ArgumentError catch (e) {
print('Failed: ${e.message}');
}
}
Profile 7
Failed: invalid id: 0
Common mistakes
- Forgetting
await. You get aFutureinstead of the value, and errors may go unhandled. Theunawaited_futureslint catches this. - Awaiting independent calls one after another when they could run
together with
Future.wait.