What is the difference between async and async* in Dart?
#dart#async#stream#future#generator#yield
Answer
Overview
Both
text
asynctext
async*- — Returns atext
async(single value in the future)textFuture<T> - — Returns atext
async*(multiple values over time)textStream<T>
async — Returns a Future
An
text
asynctext
Futuretext
awaittext
returndart// async returns a Future — one result Future<String> fetchUserName(int userId) async { final response = await http.get( Uri.parse('https://api.example.com/users/$userId'), ); final data = jsonDecode(response.body); return data['name']; // Single return value } // Usage void main() async { final name = await fetchUserName(1); print(name); // 'Alice' }
async* — Returns a Stream
An
text
async*text
yieldtext
yield*dart// async* returns a Stream — multiple values over time Stream<int> countDown(int from) async* { for (int i = from; i >= 0; i--) { await Future.delayed(const Duration(seconds: 1)); yield i; // Emit each value to the stream } } // Usage void main() async { await for (final count in countDown(5)) { print(count); // 5, 4, 3, 2, 1, 0 (one per second) } }
yield vs return
| Keyword | Used With | Behavior |
|---|---|---|
text | text | Delivers the final value and exits the function |
text | text | Emits a value and continues executing |
text | text | Delegates to another Stream (forwards all its values) |
dart// yield vs return Future<int> getOne() async { return 42; // Function ends here } Stream<int> getMany() async* { yield 1; // Emits 1, continues... yield 2; // Emits 2, continues... yield 3; // Emits 3, continues... // Function ends after last yield }
yield* (Yield Star) — Delegating Streams
text
yield*dartStream<int> ones() async* { yield 1; yield 1; yield 1; } Stream<int> twos() async* { yield 2; yield 2; } // yield* delegates to other streams Stream<int> onesAndTwos() async* { yield* ones(); // Emits: 1, 1, 1 yield* twos(); // Emits: 2, 2 } // Result stream: 1, 1, 1, 2, 2
Practical Flutter Examples
async — API Call
dartclass UserRepository { Future<User> getUser(String id) async { final response = await _dio.get('/users/$id'); return User.fromJson(response.data); } } // In Widget FutureBuilder<User>( future: repository.getUser('123'), builder: (context, snapshot) { if (snapshot.hasData) return Text(snapshot.data!.name); return const CircularProgressIndicator(); }, )
async* — Real-Time Updates
dartclass StockRepository { Stream<double> watchStockPrice(String symbol) async* { while (true) { final price = await _fetchPrice(symbol); yield price; // Emit latest price await Future.delayed(const Duration(seconds: 5)); } } } // In Widget StreamBuilder<double>( stream: repository.watchStockPrice('AAPL'), builder: (context, snapshot) { if (snapshot.hasData) return Text('\$${snapshot.data!.toStringAsFixed(2)}'); return const CircularProgressIndicator(); }, )
Key Differences
| Feature | text | text |
|---|---|---|
| Returns | text | text |
| Values | Single value | Multiple values over time |
| Emits with | text | text text |
| Listens with | text | text text |
| Widget | text | text |
| Use case | API calls, file I/O | Real-time data, polling, WebSocket |
| Completes | After text | After function body ends |
Also: sync* (Synchronous Generator)
For completeness,
text
sync*text
Iterable<T>dartIterable<int> range(int start, int end) sync* { for (int i = start; i <= end; i++) { yield i; // Lazily generated } } // Usage for (final n in range(1, 5)) { print(n); // 1, 2, 3, 4, 5 }
Key Insight: Use
when you need one result (like an API response). Usetextasyncwhen you need a continuous flow of results (like real-time updates, sensor data, or periodic polling).textasync*