Async Programming
## Learning Objectives
- Understand async programming concepts
- Master Future and async/await
- Work with Streams
- Handle errors in async code
## Why Async?
### The Problem
```dart
// Synchronous - blocks until complete
void main() {
print('Before');
var data = fetchData(); // Blocks for 5 seconds
print(data);
print('After');
}
```
### The Solution
```dart
// Asynchronous - doesn't block
void main() async {
print('Before');
var data = await fetchData(); // Non-blocking
print(data);
print('After');
}
```
## Future
### Basic Concept
```dart
void main() {
// simulate async operation
Future fetchData() {
return Future.delayed(Duration(seconds: 2), () {
return 'Data loaded';
});
}
print('Before');
fetchData().then((data) {
print(data); // Data loaded
});
print('After'); // Prints before data (non-blocking)
}
```
### Future States
1. **Uncompleted**: Operation in progress
2. **Completed with value**: Success
3. **Completed with error**: Failure
## async and await
### Basic Syntax
```dart
Future fetchUser() async {
// Simulate network delay
await Future.delayed(Duration(seconds: 1));
return 'Alice';
}
void main() async {
print('Fetching...');
String user = await fetchUser();
print('User: $user');
}
```
### async Function Rules
```dart
// async function always returns Future
Future addOne(int n) async {
await Future.delayed(Duration(milliseconds: 100));
return n + 1;
}
// void async - for side effects only
Future process() async {
await Future.delayed(Duration(seconds: 1));
print('Done');
}
```
### Multiple awaits
```dart
Future loadData() async {
print('Loading...');
var user = await fetchUser();
var posts = await fetchPosts(user);
print('User: $user');
print('Posts: $posts');
}
```
## Error Handling
### Try-Catch
```dart
Future fetchData() async {
try {
var result = await riskyOperation();
print('Success: $result');
} catch (e) {
print('Error: $e');
}
}
Future riskyOperation() async {
await Future.delayed(Duration(seconds: 1));
throw Exception('Failed!');
}
```
### Catch Specific Errors
```dart
Future process() async {
try {
await fetchData();
} on NetworkException {
print('Network error');
} on ServerException catch (e) {
print('Server error: $e');
} catch (e, stack) {
print('Unexpected: $e');
print('Stack: $stack');
}
}
class NetworkException implements Exception {}
class ServerException implements Exception {
final String message;
ServerException(this.message);
}
```
### Finally
```dart
Future process() async {
try {
await fetchData();
} catch (e) {
print('Error: $e');
} finally {
// Always executes
await cleanup();
}
}
```
## Future Methods
### then
```dart
Future fetchNumber() async {
return 42;
}
void main() {
fetchNumber()
.then((n) => n * 2)
.then((n) => print('Result: $n')); // Result: 84
}
```
### catchError
```dart
Future fetchNumber() async {
throw Exception('Error');
}
void main() {
fetchNumber()
.catchError((e) => print('Caught: $e'))
.then((n) => print('Number: $n')); // Caught: Exception: Error
}
```
### whenComplete
```dart
Future process() async {
await fetchData()
.whenComplete(() => print('Cleanup done'));
}
```
### wait
```dart
Future main() async {
var futures = [
fetchUser(1),
fetchUser(2),
fetchUser(3),
];
var results = await Future.wait(futures);
print('All users: $results');
}
```
## Streams
### Stream Basics
```dart
Stream countStream() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() async {
await for (var value in countStream()) {
print(value); // 1, 2, 3, 4, 5 (each second)
}
print('Done');
}
```
### Stream Methods
```dart
void main() {
var stream = Stream.fromIterable([1, 2, 3, 4, 5]);
// listen
stream.listen((value) {
print('Got: $value');
});
// map
stream.map((n) => n * 2);
// where
stream.where((n) => n % 2 == 0);
// take
stream.take(3);
}
```
### Single Subscription Streams
```dart
Stream countStream() async* {
for (int i = 1; i <= 5; i++) {
yield i;
}
}
void main() async {
var stream = countStream();
// Can only listen once
stream.listen(print);
// stream.listen(print); // Error: already listening
}
```
### Broadcast Streams
```dart
class EventBus {
final _controller = StreamController.broadcast();
Stream get stream => _controller.stream;
void emit(String event) {
_controller.add(event);
}
void dispose() {
_controller.close();
}
}
void main() {
var bus = EventBus();
bus.stream.listen((e) => print('Listener 1: $e'));
bus.stream.listen((e) => print('Listener 2: $e'));
bus.emit('Hello'); // Both listeners receive
bus.dispose();
}
```
## Async Patterns
### FutureBuilder (Flutter)
```dart
// Flutter example
FutureBuilder(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text('Data: ${snapshot.data}');
}
},
)
```
### StreamBuilder (Flutter)
```dart
// Flutter example
StreamBuilder(
stream: countStream(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
return Text('Count: ${snapshot.data}');
},
)
```
## Common Async Operations
### timeout
```dart
Future fetchData() async {
return Future.delayed(Duration(seconds: 5), () => 'Data');
}
void main() async {
try {
var result = await fetchData().timeout(
Duration(seconds: 2),
onTimeout: () => 'Timeout default',
);
print(result); // Timeout default
} catch (e) {
print('Error: $e');
}
}
```
### retry
```dart
Future fetchWithRetry() async {
for (int i = 0; i < 3; i++) {
try {
return await fetchData();
} catch (e) {
if (i == 2) rethrow;
await Future.delayed(Duration(seconds: 1));
}
}
throw Exception('Failed');
}
```
## Async vs Sync
| Aspect | Async | Sync |
|--------|-------|------|
| Blocking | No | Yes |
| Returns | Future | Value |
| Keyword | async/await | None |
| Error | try/catch | try/catch |
| Use case | I/O, network | Computation |
## Summary
- Future: represents a value available sometime later
- `async` makes function return Future
- `await` pauses until Future completes
- Streams: sequence of async events
- `async*` yields multiple values from stream
- `listen` subscribes to streams
- Handle errors with try/catch
- `StreamController` creates streams programmatically
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →