Lesson 16 / 25
Futures and async/await
Write asynchronous code with Future, async, await and Future.wait.
Waiting without blocking
Dart runs your code on a single thread per isolate with an event loop, so long-running work such as network requests, file I/O and timers is asynchronous. A Future<T> represents a value (or error) that will be available later. Mark a function async to use await, which pauses the function until the future completes without blocking the thread, so the UI stays responsive. Errors from awaited futures are thrown at the await and caught with ordinary try/catch. Run independent operations concurrently by starting them first and awaiting them together with Future.wait (or the record form (f1, f2).wait, which keeps each result's type). Add timeouts with future.timeout(const Duration(seconds: 5)). Common mistakes: forgetting await (the code continues before the work finishes, and errors go unhandled; the unawaited_futures lint catches this), and using forEach with an async callback, which does not wait; use for with await instead, or Future.wait for parallel work.
The event loop
One thread processes events from queues; awaiting a future lets other events run in the meantime.
Concurrent requests with Future.wait and records
Both calls run at the same time; types are preserved with the record form.
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<Map<String, dynamic>> getJson(Uri uri) async {
final response = await http.get(uri).timeout(const Duration(seconds: 5));
if (response.statusCode != 200) {
throw http.ClientException('HTTP ${response.statusCode}', uri);
}
return jsonDecode(response.body) as Map<String, dynamic>;
}
Future<int> fetchStock(String sku) async => 10; // stand-in for a real call
Future<void> loadProductPage(String id) async {
final base = Uri.parse('https://api.example.com');
try {
final (product, stock) = await (
getJson(base.resolve('products/$id')),
fetchStock(id),
).wait; // run concurrently, keep types
print('${product['title']}: $stock in stock');
} on http.ClientException catch (e) {
print('Could not load product: ${e.message}');
}
}
Future<void> main() async {
await loadProductPage('pen-blue');
}Never forget await
A call like saveOrder(order); without await returns immediately, and any error it throws is reported as an unhandled exception later. Enable the unawaited_futures lint and use unawaited(...) from dart:async only for deliberate fire-and-forget work.
Quick check: How do you run two independent futures concurrently and wait for both?
- await each one in sequence
- Use forEach with async
- Start both and await Future.wait (or the record .wait)
- Wrap them in a Stream
Answer
Start both and await Future.wait (or the record .wait) — Starting both first and waiting together lets them run at the same time.