What is the difference between async and async* in Dart?

#dart#async#stream#future#generator#yield

Answer

Overview

Both

text
async
and
text
async*
mark functions as asynchronous, but they return different types:

  • text
    async
    — Returns a
    text
    Future<T>
    (single value in the future)
  • text
    async*
    — Returns a
    text
    Stream<T>
    (multiple values over time)

async — Returns a Future

An

text
async
function performs an asynchronous operation and returns a single result wrapped in a
text
Future
. Use
text
await
to pause execution until the result is ready, and
text
return
to deliver the final value.

dart
// 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*
function is an asynchronous generator that returns a Stream of multiple values over time. Use
text
yield
to emit each value, and
text
yield*
to delegate to another stream.

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

KeywordUsed WithBehavior
text
return
text
async
Delivers the final value and exits the function
text
yield
text
async*
Emits a value and continues executing
text
yield*
text
async*
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*
forwards all values from another stream:

dart
Stream<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

dart
class 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

dart
class 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
async
text
async*
Returns
text
Future<T>
text
Stream<T>
ValuesSingle valueMultiple values over time
Emits with
text
return
text
yield
/
text
yield*
Listens with
text
await
text
await for
/
text
.listen()
Widget
text
FutureBuilder
text
StreamBuilder
Use caseAPI calls, file I/OReal-time data, polling, WebSocket
CompletesAfter
text
return
After function body ends

Also: sync* (Synchronous Generator)

For completeness,

text
sync*
returns an
text
Iterable<T>
synchronously:

dart
Iterable<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

text
async
when you need one result (like an API response). Use
text
async*
when you need a continuous flow of results (like real-time updates, sensor data, or periodic polling).