Dart Concurrency
In this lesson, you will learn how Dart handles concurrency and how applications can perform multiple tasks efficiently without blocking execution.
Concurrency is essential for building responsive applications, especially when dealing with file operations, APIs, background tasks, and heavy computations.
What Is Concurrency?
Concurrency means handling multiple tasks at the same time. In Dart, this does not mean running everything in parallel by default.
Instead, Dart uses an event-driven, non-blocking model that keeps applications fast and responsive.
Why Concurrency Is Important
- Prevents application freezing
- Improves performance
- Handles network requests efficiently
- Processes background tasks safely
Without concurrency, a single slow operation could block your entire program.
Dart Concurrency Model
Dart uses a single-threaded event loop model with:
- Event Queue
- Microtask Queue
- Isolates (for true parallelism)
This design avoids common threading issues like race conditions.
Event Loop in Dart
The event loop processes tasks in this order:
- Microtasks
- Event queue tasks
Understanding this order helps predict execution behavior.
Simple Concurrency Example
This example shows non-blocking execution using Future.
void main() {
print("Start");
Future.delayed(Duration(seconds: 2), () {
print("Async Task Completed");
});
print("End");
}
Output order:
- Start
- End
- Async Task Completed
The program does not wait for the delayed task.
Using Futures for Concurrency
Futures represent values that will be available later. They allow Dart to run tasks asynchronously.
Future fetchOrderTotal() async {
await Future.delayed(Duration(seconds: 1));
return 250;
}
void main() async {
int total = await fetchOrderTotal();
print("Order total: $total");
}
This pattern is widely used in real applications.
Concurrency with Multiple Futures
You can execute multiple async tasks concurrently using Future.wait.
Future fetchUser() async {
await Future.delayed(Duration(seconds: 1));
return "Alice";
}
Future fetchBalance() async {
await Future.delayed(Duration(seconds: 2));
return 5000;
}
void main() async {
var results = await Future.wait([
fetchUser(),
fetchBalance()
]);
print("User: ${results[0]}");
print("Balance: ${results[1]}");
}
Both tasks run concurrently instead of sequentially.
Microtasks in Dart
Microtasks have higher priority than event tasks. They are executed before the event queue.
void main() {
scheduleMicrotask(() {
print("Microtask executed");
});
Future(() {
print("Event task executed");
});
}
Microtasks are useful for small, urgent operations.
Using Isolates for True Parallelism
Dart uses Isolates to run code in parallel. Each isolate has its own memory and event loop.
This is useful for CPU-intensive tasks.
import 'dart:isolate';
void heavyTask(SendPort sendPort) {
int sum = 0;
for (int i = 0; i < 100000000; i++) {
sum += i;
}
sendPort.send(sum);
}
void main() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(heavyTask, receivePort.sendPort);
receivePort.listen((message) {
print("Result from isolate: $message");
});
}
This prevents blocking the main thread.
Real-World Use Cases
- Fetching data from multiple APIs
- Processing files in background
- Handling user interactions smoothly
- Running heavy calculations
Common Mistakes
- Blocking code in async functions
- Overusing isolates unnecessarily
- Ignoring error handling in Futures
📝 Practice Exercises
Exercise 1
Create two Futures that complete after different delays and print their results.
Exercise 2
Use Future.wait to fetch multiple values concurrently.
Exercise 3
Run a CPU-intensive loop using an isolate.
What’s Next?
In the next lesson, you will learn about Dart Asynchronous Patterns and how to design scalable async workflows.