import 'dart:async'; import 'async_typeof.dart'; import 'cancel_future.dart'; class AsyncUtil { AsyncUtil._(); static CancelableFuture retryWithExponentialBackoff( FutureCallback callback, int maxRetry, Predicate? predicate) { const Duration initialInterval = Duration(seconds: 1); int retryCount = 0; Timer? timer; void attempt(Completer completer) { callback().then((value) { if (!completer.isCompleted) { completer.complete(value); } }).catchError((error) { if (retryCount < maxRetry && (predicate == null || predicate(error))) { retryCount++; Duration nextInterval = initialInterval * (1 << (retryCount - 1)); timer = Timer(nextInterval, () => attempt(completer)); } else { if (!completer.isCompleted) { completer.completeError(error); } } }); } return CancelableFuture((completer) { attempt(completer); }, () { timer?.cancel(); }); } static CancelableFuture retryWhen(FutureCallback callback, int maxRetry, Duration interval, Predicate predicate, [Duration? timeout]) { int retryCount = 0; Timer? timer; Timer? timeoutTimer; void attempt(Completer completer) { callback().then((value) { if (!completer.isCompleted) { completer.complete(value); } }).catchError((error) { if ((maxRetry <= 0 || retryCount < maxRetry) && predicate(error)) { retryCount++; timer = Timer(interval, () => attempt(completer)); } else { if (!completer.isCompleted) { completer.completeError(error); } } }); } return CancelableFuture((completer) { if (timeout != null) { timeoutTimer = Timer(timeout, () { if (!completer.isCompleted) { completer.completeError(TimeoutException('Operation timed out')); } }); } attempt(completer); }, () { timer?.cancel(); timeoutTimer?.cancel(); }); } static CancelableFuture retry( FutureCallback callback, int maxRetry, Duration interval, [Duration? timeout]) { return retryWhen(callback, maxRetry, interval, (error) => true, timeout); } static CancelableFuture delay( FutureCallback callback, Duration interval) { Timer? timer; return CancelableFuture((completer) { timer = Timer(interval, () { callback().then((value) { if (!completer.isCompleted) { completer.complete(value); } }).catchError((error) { if (!completer.isCompleted) { completer.completeError(error); } }); }); }, () { timer?.cancel(); }); } static StreamController interval( IntervalCallback callback, Duration interval, int times, {Duration? delay}) { Timer? timer; StreamController controller = StreamController(onCancel: () { timer?.cancel(); }); int count = 0; void tick() { callback(count).then((value) { controller.add(value); count++; if (count < times) { timer = Timer(interval, tick); } else { controller.close(); } }).catchError((error) { controller.addError(error); controller.close(); }); } if (delay != null && delay > Duration.zero) { timer = Timer(delay, tick); } else { tick(); } return controller; } }