|
|
@@ -1,13 +1,21 @@
|
|
|
import 'dart:async';
|
|
|
|
|
|
-import 'async_typeof.dart';
|
|
|
-import 'cancel_future.dart';
|
|
|
+typedef FutureCallback<T> = Future<T> Function();
|
|
|
+
|
|
|
+typedef IntervalCallback<T> = Future<T> Function(int times);
|
|
|
+
|
|
|
+typedef CancelCallback<T> = void Function();
|
|
|
+
|
|
|
+typedef FutureCompleter<T> = void Function(Completer<T> completer);
|
|
|
+
|
|
|
+typedef Predicate<T> = bool Function(T? value);
|
|
|
|
|
|
class AsyncUtil {
|
|
|
AsyncUtil._();
|
|
|
|
|
|
static CancelableFuture<T> retryWithExponentialBackoff<T>(
|
|
|
- FutureCallback<T> callback, int maxRetry, Predicate<dynamic>? predicate) {
|
|
|
+ FutureCallback<T> callback, int maxRetry,
|
|
|
+ {Predicate<dynamic>? predicate}) {
|
|
|
const Duration initialInterval = Duration(seconds: 1);
|
|
|
int retryCount = 0;
|
|
|
Timer? timer;
|
|
|
@@ -37,9 +45,9 @@ class AsyncUtil {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- static CancelableFuture<T> retryWhen<T>(FutureCallback<T> callback,
|
|
|
- int maxRetry, Duration interval, Predicate<dynamic> predicate,
|
|
|
- [Duration? timeout]) {
|
|
|
+ static CancelableFuture<T> retry<T>(
|
|
|
+ FutureCallback<T> callback, Duration interval,
|
|
|
+ {Duration? timeout, int? maxRetry, Predicate<dynamic>? predicate}) {
|
|
|
int retryCount = 0;
|
|
|
Timer? timer;
|
|
|
Timer? timeoutTimer;
|
|
|
@@ -50,7 +58,8 @@ class AsyncUtil {
|
|
|
completer.complete(value);
|
|
|
}
|
|
|
}).catchError((error) {
|
|
|
- if ((maxRetry <= 0 || retryCount < maxRetry) && predicate(error)) {
|
|
|
+ if ((maxRetry == null || maxRetry <= 0 || retryCount < maxRetry) &&
|
|
|
+ (predicate == null || predicate(error))) {
|
|
|
retryCount++;
|
|
|
timer = Timer(interval, () => attempt(completer));
|
|
|
} else {
|
|
|
@@ -76,12 +85,6 @@ class AsyncUtil {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- static CancelableFuture<T> retry<T>(
|
|
|
- FutureCallback<T> callback, int maxRetry, Duration interval,
|
|
|
- [Duration? timeout]) {
|
|
|
- return retryWhen(callback, maxRetry, interval, (error) => true, timeout);
|
|
|
- }
|
|
|
-
|
|
|
static CancelableFuture<T> delay<T>(
|
|
|
FutureCallback<T> callback, Duration interval) {
|
|
|
Timer? timer;
|
|
|
@@ -134,3 +137,70 @@ class AsyncUtil {
|
|
|
return controller;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+abstract interface class Cancelable {
|
|
|
+ void cancel();
|
|
|
+}
|
|
|
+
|
|
|
+class CancelableFuture<T> implements Future<T>, Cancelable {
|
|
|
+ final Completer<T> _completer = Completer<T>();
|
|
|
+
|
|
|
+ final CancelCallback? _cancelable;
|
|
|
+
|
|
|
+ CancelableFuture(FutureCompleter<T> completer, this._cancelable) {
|
|
|
+ completer.call(_completer);
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void cancel() {
|
|
|
+ _cancelable?.call();
|
|
|
+ if (!_completer.isCompleted) {
|
|
|
+ _completer.completeError(CancelledError());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Stream<T> asStream() => _completer.future.asStream();
|
|
|
+
|
|
|
+ @override
|
|
|
+ Future<T> catchError(Function onError, {bool Function(Object error)? test}) =>
|
|
|
+ _completer.future.catchError(onError, test: test);
|
|
|
+
|
|
|
+ @override
|
|
|
+ Future<R> then<R>(FutureOr<R> Function(T value) onValue,
|
|
|
+ {Function? onError}) =>
|
|
|
+ _completer.future.then(onValue, onError: onError);
|
|
|
+
|
|
|
+ @override
|
|
|
+ Future<T> timeout(Duration timeLimit, {FutureOr<T> Function()? onTimeout}) =>
|
|
|
+ _completer.future.timeout(timeLimit, onTimeout: onTimeout);
|
|
|
+
|
|
|
+ @override
|
|
|
+ Future<T> whenComplete(FutureOr<void> Function() action) =>
|
|
|
+ _completer.future.whenComplete(action);
|
|
|
+}
|
|
|
+
|
|
|
+class CancelledError extends Error {
|
|
|
+ @override
|
|
|
+ String toString() {
|
|
|
+ return 'Operation was cancelled';
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension CancelableFutureExtension<T> on Future<T> {
|
|
|
+ CancelableFuture<T> asCancelable(
|
|
|
+ FutureCompleter completer, CancelCallback? cancelable) {
|
|
|
+ CancelableFuture<T> 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;
|
|
|
+ }
|
|
|
+}
|