Zones, Timers and Advanced Async Patterns

28.1 Timers

import 'dart:async';
final timer = Timer(const Duration(seconds: 2), () => print('fired once'));
final periodic = Timer.periodic(const Duration(seconds: 1), (t) {
  print('tick ${t.tick}');
  if (t.tick == 3) t.cancel();
});
timer.cancel();
Timer.run(() => print('next event loop turn'));

28.2 Zones

Zones intercept async callbacks, errors, timers, and print (useful for testing, logging, and error reporting such as Sentry/Crashlytics in Flutter: PlatformDispatcher.instance.onError).

void main() {
  runZonedGuarded(() {
    Future.delayed(Duration.zero, () => throw StateError('async boom'));   // uncaught async error
  }, (error, stack) {
    print('Caught by zone: $error');          // a global async error handler
  }, zoneValues: {#requestId: 'req-42'});
}
// Zone.current[#requestId] reads zone-local values (like context propagation)

28.3 Cancellation, Debounce and Retry Patterns

Cancellation: Dart futures aren't cancellable themselves. Use StreamSubscription.cancel, the async package's CancelableOperation, flags/tokens, or Completer patterns.

// Debounce
Timer? _debounce;
void onSearchChanged(String query) {
  _debounce?.cancel();
  _debounce = Timer(const Duration(milliseconds: 300), () => search(query));
}

// Retry with exponential backoff
Future<T> retry<T>(Future<T> Function() action, {int attempts = 3}) async {
  var delay = const Duration(milliseconds: 200);
  for (var i = 1; ; i++) {
    try {
      return await action();
    } catch (e) {
      if (i >= attempts) rethrow;
      await Future<void>.delayed(delay);
      delay *= 2;
    }
  }
}