Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The yield* (yield-each) statement is a delegation operator used within Dart generator functions (sync* or async*) to emit all values from another Iterable or Stream sequentially. Instead of yielding a single element, yield* unpacks the provided collection or stream and delegates the emission of values to it until it is exhausted.

Syntax

yield* expression;
  • expression: Must evaluate to an Iterable<T> when used inside a synchronous generator (sync*), or a Stream<T> when used inside an asynchronous generator (async*).

Execution Flow and Mechanics

When the Dart runtime encounters a yield* statement, the following sequence occurs:
  1. Suspension: The execution of the current (outer) generator is paused.
  2. Delegation: The runtime begins iterating over the Iterable or listening to the Stream provided by the expression.
  3. Emission: Every value produced by the target expression is forwarded directly to the consumer of the outer generator.
  4. Resumption: Once the target expression is fully consumed (the Iterable is exhausted or the Stream emits a done event), the outer generator resumes execution at the statement immediately following the yield*.

Synchronous Generator Mechanics (sync*)

In a sync* function, yield* expects an Iterable. It acts as a flattened iteration, preventing the need for manual for-in loops to emit nested collections.
Iterable<int> subSequence() sync* {
  yield 2;
  yield 3;
}

Iterable<int> mainSequence() sync* {
  yield 1;
  yield* subSequence(); // Delegates iteration to subSequence
  yield 4;
}
// Resulting sequence: 1, 2, 3, 4

Asynchronous Generator Mechanics (async*)

In an async* function, yield* expects a Stream. The outer stream will not emit subsequent values or close until the delegated stream completes.
Stream<int> subStream() async* {
  yield 2;
  yield 3;
}

Stream<int> mainStream() async* {
  yield 1;
  yield* subStream(); // Delegates event emission to subStream
  yield 4;
}
// Resulting sequence: 1, 2, 3, 4

Type Constraints and Compatibility

The type yielded by the delegated expression must match the generic type of the outer generator. Furthermore, Dart enforces strict isolation between synchronous and asynchronous generators:
  • You cannot use yield* on a Stream inside a sync* function.
  • You cannot use yield* on an Iterable inside an async* function directly. To yield an Iterable inside an async* function, it must first be converted to a stream using Stream.fromIterable().
Stream<int> mixedStream(Iterable<int> numbers) async* {
  // yield* numbers; // Compile-time error: expects a Stream
  yield* Stream.fromIterable(numbers); // Valid mechanical conversion
}

Recursive Optimization

Mechanically, yield* is highly optimized for recursive generators. If a generator calls itself recursively using yield*, Dart flattens the iterator chain. This prevents the creation of deeply nested iterator states, avoiding stack overflow errors and reducing memory overhead that would otherwise occur if elements were manually extracted and yielded one by one.
Master Dart with Deep Grasping Methodology!Learn More