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;
}
}
}