Stream<T>.periodic constructor

Stream<T>.periodic(
  1. Duration period, [
  2. T computation(
    1. int computationCount
    )?
])

Creates a stream that repeatedly emits events at period intervals.

The event values are computed by invoking computation. The argument to this callback is an integer that starts with 0 and is incremented for every event.

The period must be a non-negative Duration.

If computation is omitted, the event values will all be null.

The computation must not be omitted if the event type T does not allow null as a value.

Example:

final stream =
    Stream<int>.periodic(const Duration(
        seconds: 1), (count) => count * count).take(5);

stream.forEach(print); // Outputs event values 0,1,4,9,16.

Implementation

factory Stream.periodic(Duration period,
    [T computation(int computationCount)?]) {
  if (computation == null && !typeAcceptsNull<T>()) {
    throw ArgumentError.value(null, "computation",
        "Must not be omitted when the event type is non-nullable");
  }
  var controller = _SyncStreamController<T>(null, null, null, null);
  // Counts the time that the Stream was running (and not paused).
  Stopwatch watch = new Stopwatch();
  controller.onListen = () {
    int computationCount = 0;
    void sendEvent(_) {
      watch.reset();
      if (computation != null) {
        T event;
        try {
          event = computation(computationCount++);
        } catch (e, s) {
          controller.addError(e, s);
          return;
        }
        controller.add(event);
      } else {
        controller.add(null as T); // We have checked that null is T.
      }
    }

    Timer timer = Timer.periodic(period, sendEvent);
    controller
      ..onCancel = () {
        timer.cancel();
        return Future._nullFuture;
      }
      ..onPause = () {
        watch.stop();
        timer.cancel();
      }
      ..onResume = () {
        Duration elapsed = watch.elapsed;
        watch.start();
        timer = new Timer(period - elapsed, () {
          timer = Timer.periodic(period, sendEvent);
          sendEvent(null);
        });
      };
  };
  return controller.stream;
}