Differences Between async* and sync* Functions in Dart

In Dart, both async* and sync* functions are used to return a stream of values, but they serve different purposes and operate in distinct ways. Understanding the differences between these two types of functions is essential for effectively managing asynchronous and synchronous data streams. This guide will explain the differences, along with sample code and explanations.

1. Overview of async* Functions

An async* function is used to create a stream of asynchronous values. It allows you to yield values over time, making it suitable for scenarios where data is produced asynchronously, such as fetching data from a network or reading from a file.

Characteristics of async* Functions

  • Returns a Stream of values.
  • Uses the await keyword to pause execution until a Future completes.
  • Can yield multiple values using the yield keyword.

Example of an async* Function

import 'dart:async';

Stream<int> countDown(int start) async* {
for (int i = start; i >= 0; i--) {
await Future.delayed(Duration(seconds: 1)); // Simulate a delay
yield i; // Yield the current value
}
}

void main() async {
print('Countdown:');
await for (var value in countDown(5)) {
print(value); // Output: 5, 4, 3, 2, 1, 0
}
}
</int>

In this example:

  • The countDown function is defined as an async* function, yielding values from 5 to 0 with a delay of one second between each yield.
  • The await for loop is used to listen for values emitted by the stream.

2. Overview of sync* Functions

A sync* function is used to create a stream of synchronous values. It is suitable for scenarios where the values are generated immediately and do not require any asynchronous operations.

Characteristics of sync* Functions

  • Returns a Stream of values.
  • Does not use the await keyword, as it does not involve asynchronous operations.
  • Can yield multiple values using the yield keyword.

Example of a sync* Function

Stream<int> generateNumbers(int count) sync* {
for (int i = 0; i < count; i++) {
yield i; // Yield the current value
}
}

void main() async {
print('Generated Numbers:');
await for (var value in generateNumbers(5)) {
print(value); // Output: 0, 1, 2, 3, 4
}
}
</int>

In this example:

  • The generateNumbers function is defined as a sync* function, yielding values from 0 to 4 immediately.
  • The await for loop is used to listen for values emitted by the stream.

3. Key Differences Between async* and sync* Functions

Feature async* sync*
Return Type Stream Stream
Asynchronous Operations Can use await for asynchronous operations No await keyword; synchronous operations only
Yielding Values Uses yield to emit values over time Uses yield to emit values immediately
Execution Flow Pauses execution until a Future completes Executes immediately without waiting

4. Conclusion

In summary, async* and sync* functions in Dart are both used to create streams, but they differ significantly in their execution and use cases. async* functions are ideal for scenarios involving asynchronous data generation, while sync* functions are suited for immediate value generation. Understanding these differences will help you choose the appropriate function type based on your specific needs in Dart programming.