Asynchronous Programming
Asynchronous programming allows your program to perform tasks without blocking execution. Dart provides excellent support for async operations through Future, async, and await.
Future
A Future represents a value that will be available at some point in the future.
dart
Future<String> fetchUserData() {
return Future.delayed(
Duration(seconds: 2),
() => 'User data loaded',
);
}
void main() {
print('Fetching data...');
fetchUserData().then((data) {
print(data);
});
print('Request sent');
}
// Output:
// Fetching data...
// Request sent
// User data loaded (after 2 seconds)async and await
The async and await keywords make asynchronous code look synchronous:
dart
Future<String> fetchUserData() async {
await Future.delayed(Duration(seconds: 2));
return 'User data loaded';
}
void main() async {
print('Fetching data...');
String data = await fetchUserData();
print(data);
print('Done');
}Error Handling
dart
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 1));
throw Exception('Failed to load data');
}
void main() async {
try {
String data = await fetchData();
print(data);
} catch (e) {
print('Error: $e');
} finally {
print('Cleanup');
}
}Multiple Futures
Future.wait
Wait for multiple futures to complete:
dart
Future<void> loadData() async {
var results = await Future.wait([
fetchUser(),
fetchPosts(),
fetchComments(),
]);
print('User: ${results[0]}');
print('Posts: ${results[1]}');
print('Comments: ${results[2]}');
}Streams
Streams provide a sequence of asynchronous events:
dart
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() async {
await for (var number in countStream(5)) {
print(number);
}
}Stream Methods
dart
void main() async {
Stream<int> stream = Stream.fromIterable([1, 2, 3, 4, 5]);
// Listen
stream.listen((value) {
print(value);
});
// Transform
var doubled = stream.map((n) => n * 2);
// Filter
var evens = stream.where((n) => n % 2 == 0);
}Complete Example
dart
class ApiService {
Future<Map<String, dynamic>> fetchUser(int id) async {
await Future.delayed(Duration(seconds: 1));
return {'id': id, 'name': 'User $id', 'email': 'user$id@example.com'};
}
Future<List<String>> fetchPosts(int userId) async {
await Future.delayed(Duration(seconds: 1));
return ['Post 1', 'Post 2', 'Post 3'];
}
Stream<String> fetchComments(int postId) async* {
for (int i = 1; i <= 3; i++) {
await Future.delayed(Duration(milliseconds: 500));
yield 'Comment $i on post $postId';
}
}
}
void main() async {
var api = ApiService();
try {
// Fetch user
print('Fetching user...');
var user = await api.fetchUser(1);
print('User: ${user['name']}');
// Fetch posts
print('\nFetching posts...');
var posts = await api.fetchPosts(user['id']);
print('Posts: ${posts.length}');
// Stream comments
print('\nFetching comments...');
await for (var comment in api.fetchComments(1)) {
print(comment);
}
print('\nAll data loaded!');
} catch (e) {
print('Error: $e');
}
}Best Practices
- Always use
async/awaitfor cleaner code - Handle errors with try-catch
- Use
Future.waitfor parallel operations - Prefer streams for continuous data
- Don't forget
await- common mistake!