hi-client/lib/app/utils/kr_debounce_throttle_util.dart
2026-01-07 17:33:55 -08:00

282 lines
7.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// 🔧 防抖和限流工具类 - 用于处理快速点击导致的重复请求
///
/// 使用场景:
/// 1. 防抖Debounce用户快速点击按钮等用户停止点击后再执行一次
/// - 模式切换、搜索、自动保存
/// 2. 限流Throttle在给定时间内最多执行一次
/// - 节点切换、数据刷新、VPN 启动/停止
import 'dart:async';
import 'package:get/get.dart';
class KRDebounceThrottleUtil {
// 防抖计时器缓存(防止多个防抖冲突)
static final Map<String, Timer> _debounceTimers = {};
// 限流时间戳缓存
static final Map<String, DateTime> _throttleTimestamps = {};
/// 🔧 防抖函数:等待 delay 时间无新请求后才执行
///
/// 适用于:模式切换、搜索、自动保存等
///
/// 例子:
/// ```dart
/// KRDebounceThrottleUtil.debounce(
/// key: 'mode_switch',
/// delay: Duration(milliseconds: 300),
/// action: () {
/// controller.kr_updateConnectionType(newType);
/// },
/// );
/// ```
static void debounce({
required String key,
required Duration delay,
required VoidCallback action,
}) {
// 如果有旧的待执行任务,取消它
_debounceTimers[key]?.cancel();
// 设置新的延迟任务
_debounceTimers[key] = Timer(delay, () {
action();
_debounceTimers.remove(key);
});
}
/// 🔧 异步防抖函数(支持 Future
///
/// 适用于需要等待异步操作的场景
///
/// 例子:
/// ```dart
/// await KRDebounceThrottleUtil.debounceAsync(
/// key: 'node_switch',
/// delay: Duration(milliseconds: 500),
/// action: () async {
/// await controller.kr_performNodeSwitch(tag);
/// },
/// );
/// ```
static Future<void> debounceAsync({
required String key,
required Duration delay,
required Future<void> Function() action,
}) async {
_debounceTimers[key]?.cancel();
return Future.delayed(delay).then((_) async {
await action();
_debounceTimers.remove(key);
});
}
/// 🔧 限流函数:在指定时间内最多执行一次
///
/// 返回值true 表示执行成功false 表示被限流(仍在冷却中)
///
/// 适用于节点切换、数据刷新、VPN 启动/停止等频繁操作
///
/// 例子:
/// ```dart
/// final canExecute = KRDebounceThrottleUtil.throttle(
/// key: 'refresh',
/// duration: Duration(seconds: 2),
/// );
///
/// if (canExecute) {
/// await controller.kr_refreshAll();
/// } else {
/// showToast('操作过于频繁,请稍后再试');
/// }
/// ```
static bool throttle({
required String key,
required Duration duration,
}) {
final now = DateTime.now();
final lastExecuteTime = _throttleTimestamps[key];
// 如果这是第一次执行或已经过了冷却时间
if (lastExecuteTime == null ||
now.difference(lastExecuteTime).inMilliseconds >= duration.inMilliseconds) {
_throttleTimestamps[key] = now;
return true; // 允许执行
}
return false; // 仍在冷却期,拒绝执行
}
/// 🔧 异步限流函数
///
/// 返回值true 表示执行成功false 表示被限流
///
/// 例子:
/// ```dart
/// final success = await KRDebounceThrottleUtil.throttleAsync(
/// key: 'node_switch',
/// duration: Duration(milliseconds: 2000),
/// action: () async {
/// await controller.kr_performNodeSwitch(tag);
/// },
/// );
/// ```
static Future<bool> throttleAsync({
required String key,
required Duration duration,
required Future<void> Function() action,
}) async {
if (throttle(key: key, duration: duration)) {
try {
await action();
return true;
} catch (e) {
// 如果执行失败,重置时间戳以允许重试
_throttleTimestamps.remove(key);
rethrow;
}
}
return false;
}
/// 🔧 获取某个 key 的剩余冷却时间(毫秒)
///
/// 返回值:
/// - 0 或负数:可以执行
/// - 正数:还需等待的毫秒数
static int getRemainingThrottleTime({
required String key,
required Duration duration,
}) {
final lastExecuteTime = _throttleTimestamps[key];
if (lastExecuteTime == null) return 0;
final remaining = duration.inMilliseconds -
DateTime.now().difference(lastExecuteTime).inMilliseconds;
return remaining > 0 ? remaining : 0;
}
/// 🔧 清除指定 key 的所有计时器(调试用)
static void clear({String? key}) {
if (key != null) {
_debounceTimers[key]?.cancel();
_debounceTimers.remove(key);
_throttleTimestamps.remove(key);
} else {
// 清除所有
for (var timer in _debounceTimers.values) {
timer.cancel();
}
_debounceTimers.clear();
_throttleTimestamps.clear();
}
}
/// 🔧 获取防抖和限流的统计信息(调试用)
static Map<String, dynamic> getStats() {
return {
'activeDebounces': _debounceTimers.keys.toList(),
'activeThrottles': _throttleTimestamps.keys.toList(),
'totalActiveTimers': _debounceTimers.length + _throttleTimestamps.length,
};
}
}
/// 🔧 防抖辅助类 - 用于在 Controller 中创建防抖版本的方法
///
/// 例子:
/// ```dart
/// class MyController extends GetxController {
/// late final _debouncer = KRDebouncedMethod(
/// key: 'mode_switch',
/// delay: Duration(milliseconds: 300),
/// );
///
/// void kr_updateConnectionType(KRConnectionType type) {
/// _debouncer.call(() async {
/// // 实际的业务逻辑
/// });
/// }
/// }
/// ```
class KRDebouncedMethod {
final String key;
final Duration delay;
KRDebouncedMethod({
required this.key,
this.delay = const Duration(milliseconds: 300),
});
void call(VoidCallback action) {
KRDebounceThrottleUtil.debounce(
key: key,
delay: delay,
action: action,
);
}
Future<void> callAsync(Future<void> Function() action) async {
return KRDebounceThrottleUtil.debounceAsync(
key: key,
delay: delay,
action: action,
);
}
void cancel() {
KRDebounceThrottleUtil.clear(key: key);
}
}
/// 🔧 限流辅助类 - 用于在 Controller 中创建限流版本的方法
///
/// 例子:
/// ```dart
/// class MyController extends GetxController {
/// late final _throttler = KRThrottledMethod(
/// key: 'refresh',
/// duration: Duration(seconds: 2),
/// );
///
/// void kr_refreshAll() {
/// if (_throttler.canExecute()) {
/// // 执行刷新逻辑
/// }
/// }
/// }
/// ```
class KRThrottledMethod {
final String key;
final Duration duration;
KRThrottledMethod({
required this.key,
required this.duration,
});
bool canExecute() {
return KRDebounceThrottleUtil.throttle(key: key, duration: duration);
}
Future<bool> executeAsync(Future<void> Function() action) async {
return KRDebounceThrottleUtil.throttleAsync(
key: key,
duration: duration,
action: action,
);
}
int getRemainingTime() {
return KRDebounceThrottleUtil.getRemainingThrottleTime(
key: key,
duration: duration,
);
}
void reset() {
KRDebounceThrottleUtil.clear(key: key);
}
}