โณ Lesson 3: Asynchronous Programming in Dart
Understanding the Problem: Synchronous vs Asynchronous
๐ง Core Concept
Dart runs your code in a single thread. If one operation takes a long time, a synchronous program would freeze entirely until it completes. Asynchronous programming allows Dart to start a long-running task and continue executing other code while waiting for that task to finish.
Synchronous: You order, then stare at the counter doing nothing until your coffee arrives. The whole cafรฉ line stops.
Asynchronous: You order, get a receipt, then check emails on your phone. When your name is called, you pick up the coffee. The cafรฉ keeps serving others.
| Feature | Synchronous (Blocking) | Asynchronous (Non-blocking) |
|---|---|---|
| App responsiveness | โ Freezes during long tasks | โ Stays responsive |
| Code flow | Top to bottom, step by step | Starts tasks, handles results later |
| Use cases | Quick calculations, simple logic | Network calls, file I/O, database queries |
| Return type | Direct value (String, int, etc.) | Future<Value> (a promise of a value) |
๐ฎ Visual Demo: Sync vs Async
Future: The Promise of a Value
๐ง What is a Future?
A Future is an object that represents a potential value or error that will be available at some time in the future. Think of it as a receipt you get when you place an orderโit's not the item itself, but a guarantee that you'll get it (or an explanation of why you won't).
Three States of a Future
The operation is still running. No value or error yet.
The operation succeeded with a result.
The operation failed with an exception.
Creating and Using a Future
// Function that returns a Future<String>
Future<String> fetchUserName() {
// Simulate a 2-second network call
return Future.delayed(
Duration(seconds: 2),
() => 'Alice'
);
}
void main() {
print('Fetching user...');
// Get the Future immediately (not the value)
Future<String> nameFuture = fetchUserName();
// Register a callback for when it completes
nameFuture.then((name) {
print('User name: $name');
});
print('This prints while waiting!');
}
Line-by-Line Explanation
Future<String>Future.delayed creates a Future that waits before completing.then() registers what to do when the Future completesโ๏ธ Try It: Modify the Future
Change the delay or the returned value, then click Run.
Async/Await: Writing Clean Asynchronous Code
๐ง The Problem with .then() Chains
When you need to do several async operations in sequence, .then() chains become deeply nested and hard to read. async and await let you write async code that looks like normal sequential code.
1. A function marked with
async always returns a Future.2.
await pauses only the current function, not the whole app. Everything else keeps running.
Before and After: .then() โ async/await
void main() {
fetchUser()
.then((user) {
return fetchPosts(user.id);
})
.then((posts) {
return fetchComments(posts.first.id);
})
.then((comments) {
print(comments);
})
.catchError((error) {
print('Error: $error');
});
}
Future<void> main() async {
try {
final user = await fetchUser();
final posts = await fetchPosts(user.id);
final comments = await fetchComments(posts.first.id);
print(comments);
} catch (error) {
print('Error: $error');
}
}
๐ Complete Example: Fetching User Data
Here's a realistic example showing the full pattern you'll use in real apps:
// Simulated API functions
Future<String> fetchUserProfile() async {
await Future.delayed(Duration(seconds: 1)); // Simulate network
return '{"name": "Alice", "age": 30}';
}
Future<String> fetchUserPosts() async {
await Future.delayed(Duration(seconds: 1.5));
return '["Post 1", "Post 2", "Post 3"]';
}
// Main function using async/await
Future<void> loadUserDashboard() async {
print('๐ Loading dashboard...');
// Each await pauses THIS function only
final profileJson = await fetchUserProfile();
print('โ
Profile loaded');
final postsJson = await fetchUserPosts();
print('โ
Posts loaded');
// Now use the data
print('๐ Dashboard ready!');
print('Profile: $profileJson');
print('Posts: $postsJson');
}
โ๏ธ Try It: Add Your Own Async Step
Handling Errors in Async Code
๐ง Why Error Handling Matters
Network requests can fail. Files might not exist. Servers return errors. With async code, you must catch these errors or your app will crash. Dart uses try/catch/finallyโthe same pattern as synchronous code.
The try/catch/finally Pattern
Future<void> fetchDataSafely() async {
try {
// Code that might throw an error
print('Fetching data...');
final data = await riskyOperation();
print('Success: $data');
} on TimeoutException catch (e) {
// Handle specific error types
print('The request timed out: $e');
} on FormatException catch (e) {
print('Received invalid data format: $e');
} catch (e) {
// Catch ALL other errors
print('An unexpected error occurred: $e');
} finally {
// This ALWAYS runs - perfect for cleanup
print('Cleanup: closing connections');
}
}
โข
try block: Contains the code that might fail.โข
on TypeException catch (e): Catches specific error types you expect.โข
catch (e): Catches everything else (always include this as a safety net).โข
finally: Runs no matter whatโperfect for closing files or database connections.
๐ฎ Error Simulator
Concurrent Operations: Running Multiple Futures
๐ง Sequential vs Concurrent
If you await three independent Futures one after another, they run sequentially (total time = sum of all times). With Future.wait(), they run concurrently (total time = time of the slowest one).
| Approach | Sequential (await each) | Concurrent (Future.wait) |
|---|---|---|
| How it works | Task 1 โ wait โ Task 2 โ wait โ Task 3 | All tasks start at once, wait for all |
| Time for 3 tasks (2s each) | 6 seconds | ~2 seconds |
| When to use | Each step depends on previous result | Tasks are independent of each other |
Code Comparison
// Each await waits for the previous to finish
final user = await fetchUser(); // 2s
final posts = await fetchPosts(); // 2s } Total: 6s
final friends = await fetchFriends(); // 2s
print(user);
print(posts);
print(friends);
// All three start at the same time!
final results = await Future.wait([
fetchUser(), // โ
fetchPosts(), // โ All run together
fetchFriends(), // โ
]); // Wait for ALL to finish
// results is a List with all three values
print(results[0]); // user
print(results[1]); // posts
print(results[2]); // friends
โฑ Performance Test: Sequential vs Concurrent
๐ฏ Quick Quiz: Test Your Knowledge
Score: 0/5 | Hints remaining: 3
Loading question...
Practice Projects
Apply what you've learned by building these hands-on projects.