DART / CHAPTER 06

Lazy iterables and generator demand

Intermediate

Know when transformations execute and when to materialize them.

By the end of this chapter

  • Trace deferred where and map operations
  • Create a sync* generator
  • Bound traversal before collecting results

The idea

Many Dart Iterable operations describe work that occurs only when the result is iterated. A where or map result is not necessarily a snapshot of the source. If the underlying list changes before iteration, the later traversal can observe its current contents. Call toList when the contract requires a materialized collection at that point in time.

A sync* function yields a sequence incrementally. This is useful for large or unbounded sources when a consumer needs only a prefix. Keep side effects out of reusable transformations unless their timing is intentional, because repeated iteration can repeat work. Bound an infinite sequence with take before converting it to a list, and distinguish lazy computation from a cached result.

Dart example
Iterable<int> squares() sync* {
  for (var n=1;;n++) { yield n*n; }
}
void main() { print(squares().take(3).join(",")); }
Result
1,4,9

The consumer requests only three values, so the generator never attempts to materialize its unbounded sequence.

Try it yourself

Generate successive batches or numeric steps lazily and collect only a requested prefix.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

CHECK YOUR UNDERSTANDING

Chapter checkpoint

When should toList be called if a transformation must capture current source contents?