import 'dart:async'; typedef FutureCallback = Future Function(); typedef IntervalCallback = Future Function(int times); typedef CancelCallback = void Function(); typedef FutureCompleter = void Function(Completer completer); typedef Predicate = bool Function(T? value); class AsyncUtil { AsyncUtil._(); static CancelableFuture retryWithExponentialBackoff( FutureCallback callback, int maxRetry, {Duration initialInterval = const Duration(seconds: 1), Predicate? predicate}) { 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 retry( FutureCallback callback, Duration interval, {Duration? timeout, int? maxRetry, Predicate? predicate}) { int retryCount = 0; Timer? timer; Timer? timeoutTimer; void attempt(Completer completer) { callback().then((value) { if (!completer.isCompleted) { completer.complete(value); } }).catchError((error) { if ((maxRetry == null || maxRetry <= 0 || retryCount < maxRetry) && (predicate == null || 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 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 (times == -1 || count < times) { timer = Timer(interval, tick); } else { controller.close(); } }).catchError((error) { if (!controller.isClosed) { controller.addError(error); controller.close(); } }); } if (delay != null && delay > Duration.zero) { timer = Timer(delay, tick); } else { tick(); } return controller; } static CancelableFuture>> waitForAll( Iterable> futures) { final completers = futures.map((_) => Completer()).toList(); final results = >[]; bool isCancelled = false; void attempt(Completer>> completer) async { for (int i = 0; i < futures.length; i++) { if (isCancelled) { completer.completeError(CancelledError()); return; } try { final value = await futures.elementAt(i); results.add(AsyncResult.success(value)); } catch (e, stackTrace) { results.add(AsyncResult.failure(e, stackTrace)); } finally { completers[i].complete(); } } await Future.wait(completers.map((c) => c.future)); if (!completer.isCompleted) { completer.complete(results); } } return CancelableFuture>>((completer) { attempt(completer); }, () { isCancelled = true; }); } } class AsyncResult { final T? value; final Object? error; final StackTrace? stackTrace; bool get isSuccess => error == null; AsyncResult.success(this.value) : error = null, stackTrace = null; AsyncResult.failure(this.error, this.stackTrace) : value = null; void handle({ required void Function(T) onSuccess, required void Function(Object, StackTrace?) onError, }) { if (isSuccess) { onSuccess(value as T); } else { onError(error!, stackTrace); } } } abstract interface class Cancelable { void cancel(); } class CancelableFuture implements Future, Cancelable { final Completer _completer = Completer(); final CancelCallback? _cancelable; CancelableFuture(FutureCompleter completer, this._cancelable) { completer.call(_completer); } @override void cancel() { _cancelable?.call(); if (!_completer.isCompleted) { _completer.completeError(CancelledError()); } } @override Stream asStream() => _completer.future.asStream(); @override Future catchError(Function onError, {bool Function(Object error)? test}) => _completer.future.catchError(onError, test: test); @override Future then(FutureOr Function(T value) onValue, {Function? onError}) => _completer.future.then(onValue, onError: onError); @override Future timeout(Duration timeLimit, {FutureOr Function()? onTimeout}) => _completer.future.timeout(timeLimit, onTimeout: onTimeout); @override Future whenComplete(FutureOr Function() action) => _completer.future.whenComplete(action); } class CancelledError extends Error { @override String toString() { return 'Operation was cancelled'; } } extension CancelableFutureExtension on Future { CancelableFuture asCancelable( FutureCompleter completer, CancelCallback? cancelable) { CancelableFuture cancelableFuture = CancelableFuture(completer, cancelable); then((value) { if (!cancelableFuture._completer.isCompleted) { cancelableFuture._completer.complete(value); } }).catchError((error) { if (!cancelableFuture._completer.isCompleted) { cancelableFuture._completer.completeError(error); } }); return cancelableFuture; } }