AsyncRedux节流混合Skill asyncredux-throttle-mixin

这个技能是 AsyncRedux 状态管理库中的一个节流混合(Throttle mixin),用于限制 Action 的执行频率,防止过于频繁的调用。适用于价格刷新、API 调用限速、防止按钮滥点等场景,确保数据新鲜度和服务器负载控制。关键词:节流、Action、Dart、Flutter、AsyncRedux、状态管理、混合、限频。

移动开发 0 次安装 0 次浏览 更新于 3/19/2026

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 以设置自定义持续时间。

节流工作原理(新鲜度/陈旧度)

节流使用“新鲜度窗口”概念:

  1. 第一次分派:操作立即运行,数据变为“新鲜”
  2. 在节流周期内:数据被视为新鲜,后续分派被中止
  3. 节流周期过期后:数据变为“陈旧”,允许下一次分派运行

这确保频繁触发的操作(如“刷新价格”按钮)不会使服务器过载,同时仍允许在合理间隔后更新。

// 用户在 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);
  }
}

混合兼容性

兼容:

  • CheckInternet
  • NoDialog
  • AbortWhenNoInternet
  • Retry
  • UnlimitedRetries
  • Debounce

不兼容:

  • NonReentrant(使用一个或另一个,不要同时使用)
  • OptimisticUpdate
  • OptimisticSync
  • OptimisticSyncWithPush

组合多个混合

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: