80 lines
2.8 KiB
Dart
80 lines
2.8 KiB
Dart
import 'package:easy_debounce/easy_debounce.dart';
|
||
import 'package:easy_debounce/easy_throttle.dart';
|
||
|
||
/// 异步防抖/节流工具类
|
||
class AsyncThrottle {
|
||
// 私有构造函数,防止外部实例化
|
||
AsyncThrottle._();
|
||
|
||
// 懒汉式单例 - 使用 late final 延迟初始化
|
||
static final AsyncThrottle _instance = AsyncThrottle._();
|
||
|
||
// 获取单例实例
|
||
static AsyncThrottle get instance => _instance;
|
||
|
||
// 存储异步任务锁状态
|
||
final Map<String, bool> _asyncLocks = {};
|
||
|
||
/// 异步执行方法 - 防止弱网重复点击 (无 Loading UI)
|
||
///
|
||
/// 结合了 时间策略(防抖/节流)和 异步任务状态锁。
|
||
/// 只有当满足以下两个条件时才会执行:
|
||
/// 1. 当前没有正在执行的同名任务 (Task Lock - 解决弱网长耗时问题)
|
||
/// 2. 满足时间策略 (Time Policy - 解决快速连点问题)
|
||
///
|
||
/// [tagId]: 唯一标识符
|
||
/// [onExecute]: 要执行的异步方法
|
||
/// [duration]: 时间间隔,默认 300ms
|
||
/// [enableDebounce]: 是否启用防抖模式。
|
||
/// - true: 使用防抖 (Debounce) - 延迟执行,最后一次点击生效(适合搜索、输入)
|
||
/// - false: 使用节流 (Throttle) - 立即执行,忽略后续点击(默认,适合按钮点击)
|
||
Future<void> execute(
|
||
String tagId,
|
||
Future<void> Function() onExecute, {
|
||
Duration duration = const Duration(milliseconds: 300),
|
||
bool enableDebounce = false,
|
||
}) async {
|
||
// 1. 检查异步锁 (防止上一个请求未回来时重复点击)
|
||
// 无论是防抖还是节流,只要任务还在执行中,都不应重入
|
||
if (isExecuting(tagId)) return;
|
||
|
||
if (enableDebounce) {
|
||
// === 防抖模式 (Debounce) ===
|
||
// 延迟 duration 后执行,如果在期间再次调用,会重新计时
|
||
EasyDebounce.debounce(tagId, duration, () async {
|
||
// 防抖触发时,再次检查锁(双重检查)
|
||
if (isExecuting(tagId)) return;
|
||
|
||
_asyncLocks[tagId] = true;
|
||
try {
|
||
await onExecute();
|
||
} finally {
|
||
_asyncLocks.remove(tagId);
|
||
}
|
||
});
|
||
} else {
|
||
// === 节流模式 (Throttle) - 默认 ===
|
||
// 立即执行,并在 duration 内忽略后续调用
|
||
// throttle 返回 true 表示被节流(忽略),false 表示获得了执行权
|
||
final isThrottled = EasyThrottle.throttle(tagId, duration, () {});
|
||
|
||
// 如果被节流了,直接返回
|
||
if (isThrottled) return;
|
||
|
||
// 获得执行权,加锁并执行
|
||
_asyncLocks[tagId] = true;
|
||
try {
|
||
await onExecute();
|
||
} finally {
|
||
_asyncLocks.remove(tagId);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 检查任务是否正在执行
|
||
bool isExecuting(String tagId) => _asyncLocks[tagId] ?? false;
|
||
|
||
/// 强制取消所有锁(慎用)
|
||
void clearAllLocks() => _asyncLocks.clear();
|
||
}
|