name: asyncredux-throttle-mixin description: 添加节流混合以防止操作运行过于频繁。涵盖设置节流持续时间(以毫秒为单位)、使用案例如价格刷新,以及新鲜度/陈旧度的工作原理。
节流混合
Throttle 混合将操作执行限制在每个节流周期内最多一次。当在定义的窗口内多次分派操作时,只有第一次执行运行,而后续调用静默中止。周期过期后,允许下一次分派。
基本用法
class LoadPrices extends AppAction with Throttle {
// 节流周期,以毫秒为单位(默认是 1000ms)
int get throttle => 5000; // 5 秒
Future<AppState?> reduce() async {
var prices = await fetchCurrentPrices();
return state.copy(prices: prices);
}
}
默认节流持续时间是 1000 毫秒(1 秒)。覆盖 throttle getter 以设置自定义持续时间。
节流工作原理(新鲜度/陈旧度)
节流使用“新鲜度窗口”概念:
- 第一次分派:操作立即运行,数据变为“新鲜”
- 在节流周期内:数据被视为新鲜,后续分派被中止
- 节流周期过期后:数据变为“陈旧”,允许下一次分派运行
这确保频繁触发的操作(如“刷新价格”按钮)不会使服务器过载,同时仍允许在合理间隔后更新。
// 用户在 2 秒内快速点击“刷新”5 次
// 使用 5 秒节流:
// - 第 1 次点击:操作运行,价格更新
// - 第 2-5 次点击:静默中止(数据仍“新鲜”)
// - 5 秒后点击:操作再次运行(数据现在“陈旧”)
节流与防抖对比
| 方面 | 节流 | 防抖 |
|---|---|---|
| 何时运行 | 在第一次分派时立即运行 | 分派停止后运行 |
| 阻塞 | 运行后阻塞周期 | 每次分派重置计时器 |
| 使用案例 | 价格刷新,速率限制的 API | 输入即搜索 |
绕过节流
覆盖 ignoreThrottle 以有条件地跳过速率限制:
class LoadPrices extends AppAction with Throttle {
final bool forceRefresh;
LoadPrices({this.forceRefresh = false});
int get throttle => 5000;
// 当请求强制刷新时绕过节流
bool get ignoreThrottle => forceRefresh;
Future<AppState?> reduce() async {
var prices = await fetchCurrentPrices();
return state.copy(prices: prices);
}
}
// 正常分派 - 尊重节流
dispatch(LoadPrices());
// 强制刷新 - 忽略节流
dispatch(LoadPrices(forceRefresh: true));
失败处理
默认情况下,即使在错误后,节流锁也会持续,防止立即重试:
class LoadPrices extends AppAction with Throttle {
int get throttle => 5000;
// 如果操作失败,允许立即重试
bool get removeLockOnError => true;
Future<AppState?> reduce() async {
var prices = await fetchCurrentPrices();
return state.copy(prices: prices);
}
}
手动锁控制
要获得更多控制,使用这些方法:
// 移除此特定操作类型的锁
removeLock();
// 移除所有节流操作的锁
removeAllLocks();
自定义锁定策略
覆盖 lockBuilder() 以实现不同的锁定行为:
class LoadPricesForSymbol extends AppAction with Throttle {
final String symbol;
LoadPricesForSymbol(this.symbol);
int get throttle => 5000;
// 使用符号作为锁键的一部分
// 这允许按符号而不是按操作类型进行节流
Object lockBuilder() => 'LoadPrices_$symbol';
Future<AppState?> reduce() async {
var price = await fetchPrice(symbol);
return state.copy(prices: state.prices.add(symbol, price));
}
}
// 这些可以并行运行(不同的锁键):
dispatch(LoadPricesForSymbol('AAPL'));
dispatch(LoadPricesForSymbol('GOOGL'));
// 但这将被节流(与第一个相同的锁键):
dispatch(LoadPricesForSymbol('AAPL')); // 如果在 5 秒内,则中止
常见使用案例
价格/数据刷新
class RefreshStockPrices extends AppAction with Throttle {
int get throttle => 10000; // 最多每 10 秒一次
Future<AppState?> reduce() async {
var prices = await stockApi.getAllPrices();
return state.copy(stockPrices: prices);
}
}
速率限制的 API 调用
class SyncWithServer extends AppAction with Throttle {
int get throttle => 30000; // 最多每 30 秒一次
Future<AppState?> reduce() async {
var data = await api.sync();
return state.copy(lastSync: DateTime.now(), data: data);
}
}
防止按钮滥点
class SubmitFeedback extends AppAction with Throttle {
final String feedback;
SubmitFeedback(this.feedback);
int get throttle => 60000; // 最多每分钟一次
Future<AppState?> reduce() async {
await api.submitFeedback(feedback);
return state.copy(feedbackSubmitted: true);
}
}
混合兼容性
兼容:
CheckInternetNoDialogAbortWhenNoInternetRetryUnlimitedRetriesDebounce
不兼容:
NonReentrant(使用一个或另一个,不要同时使用)OptimisticUpdateOptimisticSyncOptimisticSyncWithPush
组合多个混合
class LoadPrices extends AppAction
with CheckInternet, Throttle, Retry {
int get throttle => 5000;
Future<AppState?> reduce() async {
// CheckInternet 确保连接性
// Throttle 防止过多调用
// Retry 处理临时故障
var prices = await fetchCurrentPrices();
return state.copy(prices: prices);
}
}
参考
文档中的 URL:
- https://asyncredux.com/sitemap.xml
- https://asyncredux.com/flutter/advanced-actions/action-mixins
- https://asyncredux.com/flutter/advanced-actions/control-mixins
- https://asyncredux.com/flutter/advanced-actions/redux-action
- https://asyncredux.com/flutter/advanced-actions/before-and-after-the-reducer
- https://asyncredux.com/flutter/advanced-actions/aborting-the-dispatch
- https://asyncredux.com/flutter/advanced-actions/optimistic-mixins
- https://asyncredux.com/flutter/advanced-actions/internet-mixins
- https://asyncredux.com/flutter/basics/dispatching-actions
- https://asyncredux.com/flutter/basics/async-actions
- https://asyncredux.com/flutter/basics/failed-actions
- https://asyncredux.com/flutter/miscellaneous/refresh-indicators
- https://asyncredux.com/flutter/miscellaneous/database-and-cloud
- https://asyncredux.com/flutter/miscellaneous/wait-condition
- https://asyncredux.com/flutter/testing/mocking
- https://asyncredux.com/flutter/about
- https://asyncredux.com/flutter/intro