Asynchronous Execution
Dart is a single-threaded execution environment that relies on asynchronous primitives (Future, Stream, and async/await) to perform non-blocking I/O and heavy computational scheduling without freezing the main thread.
1. Core Primitives: Future and Stream
Future<T>: Represents a computation that delivers a single value or error asynchronously in the future (similar toPromisein JavaScript orTaskin C#).Stream<T>: Represents a sequence of asynchronous events produced over time.
Future States
A Future exists in one of two states:
- Uncompleted: The asynchronous operation is currently running or pending in the event queue.
- Completed: The operation finished, yielding either a Value (
T) or an Error (Object).
2. Under The Hood: async/await State Machine Transformation
When you label a method with async, the Dart compiler transforms the function body into an asynchronous state machine.
State Machine Breakdown
- Synchronous Execution Until First
await: Code before the firstawaitkeyword executes synchronously when the function is invoked! - Suspension & Continuation: When the execution reaches
await expr:- The expression
expris evaluated to aFuture. - The remaining portion of the function is registered as a
.then()callback continuation. - Execution control immediately returns to the caller.
- The expression
- Event Loop Resumption: When
exprcompletes, its callback is scheduled onto the event loop queue to resume execution of the remaining state machine.
[ Function Called ]
│
▼
(Executes Synchronously)
│
[ await expr ] ──► Registers Continuation on Event Loop ──► Return Control to Caller
│
▼ (When Future Completes)
[ Resume State Machine ] ──► Execute Remaining Code3. Single-Subscription vs. Broadcast Streams
Dart Streams come in two distinct flavors:
| Feature | Single-Subscription Stream | Broadcast Stream |
|---|---|---|
| Listener Count | Maximum of 1 listener. | Multiple listeners allowed. |
| Event Buffering | Buffers events until a listener subscribes. | Events are fired immediately; dropped if no listener is present. |
| Use Case | File I/O, HTTP response body streaming. | UI Event Buses, State Management (BehaviorSubject), WebSocket feeds. |
4. Error Handling & Zones (runZonedGuarded)
Asynchronous errors in Dart cannot be caught by standard outer try-catch blocks if they occur inside un-awaited background futures.
Zones as Execution Contexts
A Zone represents an isolated execution context with scoped error handlers, print overrides, and zone-local values. Wrapping code inside runZonedGuarded catches all unhandled asynchronous exceptions:
import 'dart:async';
void main() {
runZonedGuarded(() {
// Application entry point
performAsyncTask();
}, (error, stackTrace) {
// Intercepts ALL unhandled async errors in this Zone
print('Caught by Zone: $error');
});
}5. Trade-offs & Production Considerations
- Parallelism Illusion:
async/awaitdoes NOT run code on a background CPU thread! A long CPU-bound loop inside anasyncfunction will still freeze the UI thread. CPU-intensive operations require Isolates. - Memory Leak Traps: Forgetting to cancel a
StreamSubscriptioninside a Flutter Widget’sdispose()method keeps the widget reference in memory, creating severe memory leaks.