重试混入Skill asyncredux-retry-mixin

重试混入是一个用于 Dart 异步 Redux 框架的技能,它通过指数退避机制自动重试失败的动作,提高应用健壮性,适用于处理网络请求等临时性错误。关键词包括:重试、指数退避、异步、Redux、Dart、混入、移动开发、Flutter、API 调用、错误处理。

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

name: asyncredux-retry-mixin description: 为动作失败添加具有指数退避的自动重试的 Retry 混入。涵盖单独使用 Retry 进行有限重试、结合 UnlimitedRetries 进行无限重试以及配置重试行为。

重试混入

Retry 混入使用指数退避自动重试失败的动作。当 reduce() 方法中发生错误时,动作会以逐渐增加的延迟重新执行。

基本用法

Retry 混入添加到任何应在失败时自动重试的动作中:

class LoadDataAction extends AppAction with Retry {
  Future<AppState?> reduce() async {
    var data = await fetchDataFromServer();
    return state.copy(data: data);
  }
}

使用此混入:

  • 如果动作失败,它会自动重试最多 3 次(默认)
  • 每次重试等待时间比前一次更长(指数退避)
  • 如果所有重试都失败,则抛出原始错误

配置参数

覆盖这些 getter 以自定义重试行为:

class LoadDataAction extends AppAction with Retry {

  // 首次重试前的延迟(默认:350ms)
  int get initialDelay => 500;

  // 延迟增长乘数(默认:2)
  int get multiplier => 2;

  // 最大重试尝试次数(默认:3)
  int get maxRetries => 5;

  // 延迟上限以防止过度等待(默认:5000ms)
  int get maxDelay => 10000;

  Future<AppState?> reduce() async {
    var data = await fetchDataFromServer();
    return state.copy(data: data);
  }
}
参数 默认值 目的
initialDelay 350 ms 首次重试前的等待时间
multiplier 2 尝试间延迟的增长因子
maxRetries 3 最大重试次数(总执行次数 = maxRetries + 1)
maxDelay 5 秒 延迟上限以防止过度等待

重试序列示例

使用默认设置(initialDelay=350ms, multiplier=2, maxRetries=3):

  1. 初始尝试 - 动作运行,失败
  2. 等待 350ms - 首次重试,失败
  3. 等待 700ms - 第二次重试,失败
  4. 等待 1400ms - 第三次重试,失败
  5. 抛出错误 - 所有重试耗尽

时间考虑

重试延迟在 reducer 完成后开始,而不是从动作派发时开始。如果 reduce() 花费 1 秒失败且 initialDelay 为 350ms,首次重试在动作开始后 1.35 秒开始。

跟踪重试尝试

在动作中访问 attempts getter 以了解当前运行的尝试:

class LoadDataAction extends AppAction with Retry {
  Future<AppState?> reduce() async {
    print('尝试 ${attempts + 1}'); // 0 索引,所以首次尝试是 0

    if (attempts > 0) {
      // 重试时可能尝试不同的服务器
      return state.copy(data: await fetchFromBackupServer());
    }

    return state.copy(data: await fetchFromPrimaryServer());
  }
}

无限重试

UnlimitedRetriesRetry 结合以无限重试,直到动作成功:

class CriticalSyncAction extends AppAction with Retry, UnlimitedRetries {
  Future<AppState?> reduce() async {
    await syncCriticalData();
    return state.copy(syncComplete: true);
  }
}

这等同于将 maxRetries 设置为 -1

警告:await dispatchAndWait(action)UnlimitedRetries 一起使用,如果动作继续失败,可能会无限期挂起。谨慎使用,并考虑动作是否有可能最终成功。

重要行为注意事项

仅 reduce() 失败触发重试

Retry 混入仅当 reduce() 方法中发生错误时重试。before() 方法中的失败不会触发重试 - 它们立即失败。

class LoadDataAction extends AppAction with Retry {

  @override
  Future<void> before() async {
    // 这里的错误不会触发重试 - 动作立即失败
    await validatePermissions();
  }

  Future<AppState?> reduce() async {
    // 仅这里的错误触发重试机制
    return state.copy(data: await fetchData());
  }
}

动作变为异步

所有使用 Retry 混入的动作都变为异步,无论其原始同步性质如何。这是因为重试机制需要在尝试之间等待。

与非重入结合(最佳实践)

大多数使用 Retry 的动作还应包含 NonReentrant 混入以防止多个实例同时运行:

class SaveDataAction extends AppAction with NonReentrant, Retry {
  Future<AppState?> reduce() async {
    await saveToServer();
    return state.copy(saved: true);
  }
}

这防止以下场景:

  • 用户多次点击“保存”
  • 多个重试序列并行运行
  • 服务器收到重复或冲突请求

与 CheckInternet 结合

对于网络操作,将 RetryCheckInternet 结合以确保在尝试动作前有连接:

class FetchUserProfile extends AppAction with CheckInternet, Retry {
  Future<AppState?> reduce() async {
    var profile = await api.getUserProfile();
    return state.copy(profile: profile);
  }
}

CheckInternet 混入首先运行。如果没有连接,动作立即失败而不尝试重试。

常见用例

具有临时失败的 API 调用

class FetchProductsAction extends AppAction with Retry {
  int get maxRetries => 3;
  int get initialDelay => 500;

  Future<AppState?> reduce() async {
    var products = await api.getProducts();
    return state.copy(products: products);
  }
}

关键同步操作

class SyncPendingChanges extends AppAction with Retry, UnlimitedRetries {
  int get initialDelay => 1000;
  int get maxDelay => 30000; // 重试间延迟上限为 30 秒

  Future<AppState?> reduce() async {
    await syncService.pushPendingChanges();
    return state.copy(hasPendingChanges: false);
  }
}

具有扩展重试的支付处理

class ProcessPaymentAction extends AppAction with NonReentrant, Retry {
  final double amount;

  ProcessPaymentAction(this.amount);

  int get maxRetries => 5;
  int get initialDelay => 1000;
  int get multiplier => 2;
  int get maxDelay => 10000;

  Future<AppState?> reduce() async {
    var result = await paymentGateway.process(amount);
    return state.copy(paymentStatus: result.status);
  }
}

混入兼容性

兼容:

  • CheckInternet
  • NoDialog
  • AbortWhenNoInternet
  • NonReentrant
  • Throttle
  • Debounce

可结合:

  • UnlimitedRetries(启用无限重试)

完整示例与所有选项

class RobustApiAction extends AppAction
    with CheckInternet, NonReentrant, Retry {

  // 重试配置
  int get initialDelay => 500;     // 首次重试前 500ms
  int get multiplier => 2;          // 每次延迟加倍
  int get maxRetries => 4;          // 最多尝试 5 次(总次数)
  int get maxDelay => 8000;         // 等待时间不超过 8 秒

  Future<AppState?> reduce() async {
    if (attempts > 0) {
      print('重试尝试 $attempts');
    }

    var data = await api.fetchCriticalData();
    return state.copy(data: data);
  }
}

参考

文档中的 URL: