Generators in Dart are a powerful feature that allows you to produce a sequence of values lazily, meaning values are generated on demand rather than all at once. This can be particularly useful when working with large datasets or streams of data where you don't want to load everything into memory at once.
Key Concepts
- Synchronous Generators: These generate values synchronously using the
sync*
keyword. - Asynchronous Generators: These generate values asynchronously using the
async*
keyword. - Yield: The
yield
keyword is used to produce a value in a generator function.
Synchronous Generators
A synchronous generator produces values one at a time and can be iterated over using a for
loop or other iterable methods.
Example
Iterable<int> syncGenerator(int n) sync* { for (int i = 0; i < n; i++) { yield i; } } void main() { var numbers = syncGenerator(5); for (var number in numbers) { print(number); } }
Explanation
- The
sync*
keyword indicates that the function is a synchronous generator. - The
yield
keyword is used to produce each value. - The
for
loop in themain
function iterates over the generated values.
Asynchronous Generators
An asynchronous generator produces values asynchronously, which is useful when dealing with I/O operations or other asynchronous tasks.
Example
Stream<int> asyncGenerator(int n) async* { for (int i = 0; i < n; i++) { await Future.delayed(Duration(seconds: 1)); yield i; } } void main() async { await for (var number in asyncGenerator(5)) { print(number); } }
Explanation
- The
async*
keyword indicates that the function is an asynchronous generator. - The
yield
keyword is used to produce each value. - The
await
keyword is used to wait for asynchronous operations. - The
await for
loop in themain
function iterates over the generated values asynchronously.
Practical Exercises
Exercise 1: Fibonacci Sequence
Write a synchronous generator function that produces the first n
numbers in the Fibonacci sequence.
Solution
Iterable<int> fibonacci(int n) sync* { int a = 0, b = 1; for (int i = 0; i < n; i++) { yield a; int temp = a; a = b; b = temp + b; } } void main() { var fibNumbers = fibonacci(10); for (var number in fibNumbers) { print(number); } }
Exercise 2: Asynchronous Data Fetching
Write an asynchronous generator function that fetches data from a list of URLs and yields the data.
Solution
import 'dart:async'; Stream<String> fetchData(List<String> urls) async* { for (var url in urls) { await Future.delayed(Duration(seconds: 1)); // Simulate network delay yield 'Data from $url'; } } void main() async { var urls = ['url1', 'url2', 'url3']; await for (var data in fetchData(urls)) { print(data); } }
Common Mistakes and Tips
- Forgetting
sync*
orasync*
: Ensure you usesync*
for synchronous generators andasync*
for asynchronous generators. - Blocking Code in Asynchronous Generators: Avoid blocking code in asynchronous generators; use
await
for asynchronous operations. - Using
yield
Outside Generators: Theyield
keyword can only be used inside generator functions.
Summary
In this section, you learned about Dart generators, including both synchronous and asynchronous generators. You saw how to use the sync*
and async*
keywords to create generators and how to use the yield
keyword to produce values. You also practiced writing generator functions with practical exercises. Understanding generators will help you handle sequences of data more efficiently, especially in scenarios involving large datasets or asynchronous operations.
Dart Programming Course
Module 1: Introduction to Dart
- Introduction to Dart
- Setting Up the Development Environment
- Your First Dart Program
- Basic Syntax and Structure