Asynchronous Programming in Dart
In this lesson, you will learn about Asynchronous Programming in Dart. Asynchronous programming allows Dart applications to perform long-running tasks without blocking the main execution flow.
This is critical for building fast, responsive applications such as web apps, mobile apps, APIs, and command-line tools.
Why Asynchronous Programming Is Important
Some operations take time to complete, such as:
- Fetching data from the internet
- Reading or writing files
- Database queries
- Waiting for user input
If these operations run synchronously, the application may freeze or become unresponsive.
Synchronous vs Asynchronous Execution
Let’s compare synchronous and asynchronous behavior using a simple example.
Synchronous Example
void main() {
print("Start");
heavyTask();
print("End");
}
void heavyTask() {
for (int i = 0; i < 1000000000; i++) {}
}
Here, the program waits for heavyTask() to finish before printing End.
Asynchronous Concept
With asynchronous programming, Dart allows other code to run while waiting for a task to complete.
Single-Threaded but Non-Blocking
Dart runs on a single thread but uses an event loop to manage asynchronous tasks.
Instead of waiting, Dart schedules tasks and continues execution.
Understanding the Event Loop
The event loop works like a task manager:
- Executes synchronous code first
- Queues asynchronous operations
- Executes completed tasks when ready
Future: The Core of Asynchronous Dart
In Dart, asynchronous operations are represented by the Future class.
A Future represents a value that will be available later.
Creating a Future
Future fetchData() {
return Future.delayed(Duration(seconds: 2), () {
return "Data loaded successfully";
});
}
This simulates fetching data from a server.
Using Future with then()
void main() {
print("Start");
fetchData().then((result) {
print(result);
});
print("End");
}
Output order:
- Start
- End
- Data loaded successfully
This shows non-blocking behavior.
Real-World Example: API Request
Let’s simulate fetching user data from an API.
Future
Handling API Response
void main() {
getUser().then((user) {
print("User ID: ${user['id']}");
print("Name: ${user['name']}");
print("Age: ${user['age']}");
});
}
Error Handling in Futures
Asynchronous operations may fail. Dart provides error handling for futures.
Future divide(int a, int b) {
return Future(() {
if (b == 0) {
throw Exception("Division by zero");
}
return a ~/ b;
});
}
Handling Errors with catchError()
void main() {
divide(10, 0)
.then((result) => print(result))
.catchError((error) => print(error));
}
Chaining Asynchronous Operations
You can chain multiple asynchronous tasks together.
fetchData()
.then((data) => data.length)
.then((length) => print("Length: $length"));
Real-World Use Case: File Download Simulation
Future downloadFile() {
return Future.delayed(Duration(seconds: 4), () {
print("File downloaded");
});
}
This simulates a real file download operation.
Benefits of Asynchronous Programming
- Non-blocking execution
- Better user experience
- Efficient resource usage
- Scalable applications
📝 Practice Exercises
Exercise 1
Create a function that returns a future after 2 seconds.
Exercise 2
Print a message before and after calling the future.
Exercise 3
Handle an error scenario using catchError.
✅ Practice Answers
Future delayedMessage() {
return Future.delayed(Duration(seconds: 2), () => "Hello from Dart");
}
void main() {
print("Before");
delayedMessage()
.then((msg) => print(msg))
.catchError((e) => print(e));
print("After");
}
What’s Next?
In the next lesson, you will learn about Futures in Dart in depth, including states, chaining, and advanced error handling.