2024-12-03 21:47:12 +08:00
|
|
|
import 'dart:async';
|
|
|
|
|
import 'dart:ui';
|
|
|
|
|
|
2026-10-13 17:01:19 +08:00
|
|
|
import 'package:fl_clash/ui/ui.dart';
|
2025-01-09 18:38:52 +08:00
|
|
|
|
2025-07-31 17:09:18 +08:00
|
|
|
extension FutureExt<T> on Future<T> {
|
|
|
|
|
Future<T> withTimeout({
|
2024-12-03 21:47:12 +08:00
|
|
|
Duration? timeout,
|
2025-07-31 17:09:18 +08:00
|
|
|
String? tag,
|
2024-12-03 21:47:12 +08:00
|
|
|
VoidCallback? onLast,
|
|
|
|
|
FutureOr<T> Function()? onTimeout,
|
|
|
|
|
}) {
|
2025-10-14 15:13:52 +08:00
|
|
|
final realTimeout = timeout ?? const Duration(minutes: 3);
|
2026-08-05 10:55:33 +08:00
|
|
|
final cleanupTimer = onLast == null
|
|
|
|
|
? null
|
|
|
|
|
: Timer(realTimeout + commonDuration, onLast);
|
|
|
|
|
if (cleanupTimer != null) {
|
|
|
|
|
unawaited(
|
|
|
|
|
then<void>(
|
|
|
|
|
(_) => cleanupTimer.cancel(),
|
|
|
|
|
onError: (Object _, StackTrace _) {
|
|
|
|
|
cleanupTimer.cancel();
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-12-03 21:47:12 +08:00
|
|
|
return this.timeout(
|
2025-10-14 15:13:52 +08:00
|
|
|
realTimeout,
|
2024-12-03 21:47:12 +08:00
|
|
|
onTimeout: () async {
|
|
|
|
|
if (onTimeout != null) {
|
|
|
|
|
return onTimeout();
|
|
|
|
|
} else {
|
2025-07-31 17:09:18 +08:00
|
|
|
throw TimeoutException('${tag ?? runtimeType} timeout');
|
2024-12-03 21:47:12 +08:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-31 17:09:18 +08:00
|
|
|
|
2026-10-13 17:01:19 +08:00
|
|
|
/// Resolves once, then hands out the value rather than the future that
|
|
|
|
|
/// resolved it: a completed future still runs its listeners in the zone it was
|
|
|
|
|
/// created in, which under test may be a fake-async zone nothing drives.
|
|
|
|
|
class ResolveOnce<T> {
|
|
|
|
|
final Future<T> Function() _resolve;
|
|
|
|
|
Future<T>? _pending;
|
|
|
|
|
({T value})? _resolved;
|
|
|
|
|
|
|
|
|
|
ResolveOnce(this._resolve);
|
|
|
|
|
|
|
|
|
|
Future<T> get value async {
|
|
|
|
|
if (_resolved case (:final value)) return value;
|
|
|
|
|
final value = await (_pending ??= _resolve());
|
|
|
|
|
_resolved = (value: value);
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 17:09:18 +08:00
|
|
|
extension CompleterExt<T> on Completer<T> {
|
|
|
|
|
void safeCompleter(T value) {
|
|
|
|
|
if (isCompleted) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
complete(value);
|
|
|
|
|
}
|
|
|
|
|
}
|