Async and Futures | Dart - Wyatt's Notes
Why Async Matters in Dart
Section titled “Why Async Matters in Dart”Dart runs on a single-threaded event loop with an isolated memory model. Unlike languages with Threads (Java, C++, Rust), Dart uses event-driven concurrency — the single thread processes Events from a queue, interleaving async operations without blocking.
This design is fundamental to Flutter”s architecture: the UI must remain responsive (60 fps) while Performing I/O (network requests, file reads, database queries). If any operation blocks the thread, The entire UI freezes.
flowchart TD
A["Event Loop"] --> B{"Is the queue empty?"}
B -->|No| C["Dequeue next event"]
C --> D["Execute event handler"]
D --> E{"Handler complete?"}
E -->|Yes| B
E -->|No<br/>(async result pending)| F["Register callback<br/>in microtask queue"]
F --> G["Yield to event loop"]
G --> B
B -->|Yes| H["Idle<br/>(wait for next I/O event)"]
H --> BFutures
Section titled “Futures”A Future<T> represents a value that will be available at some point in the future — either a value Of type T or an error. It is Dart’s equivalent of JavaScript’s Promise or Rust’s Future.
Creating Futures
Section titled “Creating Futures”// From a computation (runs on the event loop when awaited)Future<int> computeSquare(int n) async { return n * n;}
// From a callback-based APIFuture<http.Response> fetchUser() { return http.get(Uri.parse('https://api.example.com/user'));}
// With Future.value (immediately resolved)Future<String> cachedGreeting() { return Future.value('Hello');}
// With Future.delayed (resolved after a delay)Future<void> delayedGreeting() async { await Future.delayed(Duration(seconds: 1)); print('Hello after 1 second');}
// With Future.error (immediately rejected)Future<void> fail() { return Future.error(Exception('Something went wrong'));}async/await
Section titled “async/await”The async/await syntax is syntactic sugar for working with Futures. async marks a function as Asynchronous, and await suspends execution until the Future completes:
// Without async/await (callback style)Future<void> loadData() { return http.get(Uri.parse('https://api.example.com/data')).then((response) { var data = jsonDecode(response.body); print('Got ${data['items'].length} items'); }).catchError((error) { print('Error: $error'); });}
// With async/await (linear style — easier to read and reason about)Future<void> loadData() async { try { final response = await http.get(Uri.parse('https://api.example.com/data')); final data = jsonDecode(response.body); print('Got ${data['items'].length} items'); } catch (error) { print('Error: $error'); }}Error Handling
Section titled “Error Handling”Future<int> fetchAge() async { final response = await http.get(Uri.parse('https://api.example.com/user')); if (response.statusCode != 200) { throw Exception('Failed to fetch user'); } return jsonDecode(response.body)['age'] as int;}
// Try-catch (preferred)Future<void> example() async { try { final age = await fetchAge(); print('Age: $age'); } on FormatException catch (e) { print('Invalid JSON: $e'); } on http.ClientException catch (e) { print('Network error: $e'); } catch (e) { print('Unexpected error: $e'); } finally { print('Cleanup'); }}
// catchError on the Future chainFuture<void> example() async { final age = await fetchAge().catchError((e) { print('Fallback: $e'); return 0; });}Sequential vs Parallel Execution
Section titled “Sequential vs Parallel Execution”// Sequential: each await blocks until the Future completesFuture<void> sequential() async { final user = await fetchUser(); // 1 second final orders = await fetchOrders(user.id); // 1 second final profile = await fetchProfile(user.id); // 1 second // Total: 3 seconds}
// Parallel: all Futures start immediately, await all resultsFuture<void> parallel() async { final results = await Future.wait([ fetchUser(), fetchOrders('user-1'), fetchProfile('user-1'), ]); // Total: 1 second (all run concurrently) final user = results[0]; final orders = results[1]; final profile = results[2];}
// Parallel with named resultsFuture<void> parallelNamed() async { final userFuture = fetchUser(); final ordersFuture = fetchOrders('user-1'); final profileFuture = fetchProfile('user-1');
// Each await blocks only until its own Future completes final user = await userFuture; final orders = await ordersFuture; final profile = await profileFuture;}