Futures and async/await
Handling values that aren't ready yet with Future, and writing asynchronous code that reads like synchronous code.
អាន 2 នាទី
Some operations — reading a file, making a network request, waiting on a timer — can't return their result immediately. Dart represents "a value that will exist eventually" with Future<T>, and gives you async/await to work with it without nesting callbacks.
What a Future represents
Future<String> fetchUsername() {
return Future.delayed(const Duration(seconds: 2), () => 'devlearner42');
}
void main() {
print('Fetching...');
fetchUsername().then((username) {
print('Got: $username');
});
print('This prints before the username, not after');
}Running this prints Fetching... and This prints before... immediately, then Got: devlearner42 two seconds later. fetchUsername() returns a Future<String> right away — not the string itself — and .then() registers a callback to run once that future actually completes. The program doesn't block waiting; it moves on and comes back to the callback later.
async and await
Chaining several .then() calls gets hard to read fast. async/await lets you write the same logic so it reads top-to-bottom, like synchronous code:
Future<String> fetchUsername() {
return Future.delayed(const Duration(seconds: 1), () => 'devlearner42');
}
Future<int> fetchFollowerCount(String username) {
return Future.delayed(const Duration(seconds: 1), () => 128);
}
Future<void> printProfile() async {
print('Loading profile...');
final username = await fetchUsername();
final followers = await fetchFollowerCount(username);
print('$username has $followers followers');
}
void main() {
printProfile();
}Marking printProfile async allows await inside it. await fetchUsername() pauses execution of this function — not the whole program — until the future completes, then hands back the unwrapped String rather than a Future<String>. The result reads exactly like ordinary sequential code, even though each step genuinely happens over time rather than instantly.
An async function always returns a Future itself, even if you never explicitly construct one — Future<void> here just means "runs asynchronously, doesn't produce a meaningful result."
Running futures concurrently
Awaiting one future at a time is sequential — each one waits for the last to finish. When operations don't depend on each other, Future.wait runs them concurrently instead:
Future<void> loadDashboard() async {
final results = await Future.wait([
fetchUsername(),
fetchFollowerCount('devlearner42'),
]);
print('Loaded: $results');
}Both futures start immediately and loadDashboard resumes once all of them finish — roughly the time of the slowest one, rather than the sum of both, which matters a lot once you're making several independent network calls.
Error handling with try/catch
await integrates with ordinary try/catch, rather than needing a separate error-handling mechanism:
Future<void> loadData() async {
try {
final username = await fetchUsername();
print(username);
} catch (e) {
print('Failed to load: $e');
}
}If the awaited future completes with an error instead of a value, await re-throws it at that exact line — so the same try/catch you'd use for synchronous code works here without modification. We'll go deeper on exceptions specifically in an upcoming lesson.