Files
FlClash/lib/common/future.dart
T

66 lines
1.6 KiB
Dart
Raw Normal View History

2024-12-03 21:47:12 +08:00
import 'dart:async';
import 'dart:ui';
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
/// 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);
}
}