AsyncRedux异步操作Skill asyncredux-async-actions

AsyncRedux异步操作是一种在Flutter应用中使用AsyncRedux库进行异步状态管理的技能。它允许开发者创建异步action来处理如API调用、数据库访问、文件操作等异步任务,简化异步工作流程,支持错误处理和状态管理。关键词:AsyncRedux, Flutter, 异步操作, 状态管理, API调用, 数据库操作。

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

名称: asyncredux-async-actions 描述: 为API调用、数据库操作和其他异步工作创建AsyncRedux(Flutter)异步操作。

AsyncRedux 异步操作

基本异步操作结构

当一个action的reduce()方法返回Future<AppState?>而不是AppState?时,它就变成了异步操作。用于数据库访问、API调用、文件操作或任何需要await的工作。

class FetchUser extends ReduxAction<AppState> {
  @override
  Future<AppState?> reduce() async {
    final user = await api.fetchUser();
    return state.copy(user: user);
  }
}

与传统Redux需要中间件不同,AsyncRedux简化了它:只需返回一个Future即可工作。

关键规则:每条路径都必须有await

如果action是异步的(返回Future)并改变状态(返回非空状态),框架要求所有执行路径至少包含一个await。如果实际上不等待任何东西,永远不要声明Future<AppState?>

有效模式

// 简单的异步带await
Future<AppState?> reduce() async {
  final data = await fetchData();
  return state.copy(data: data);
}

// 使用微任务(最小的有效await)
Future<AppState?> reduce() async {
  await microtask;
  return state.copy(timestamp: DateTime.now());
}

// 条件语句 - 两条路径都有await
Future<AppState?> reduce() async {
  if (state.needsRefresh) {
    return await fetchAndUpdate();
  }
  else return await validateCurrent();
}

// 总是返回null
Future<AppState?> reduce() async {
  if (state.needsRefresh) {
    await fetchAndUpdate();
  }  
  
  return null;
}

无效模式(会导致问题)

// 错误:根本没有await
Future<AppState?> reduce() async {
  return state.copy(counter: state.counter + 1);
}

// 错误:只在某些路径有await
Future<AppState?> reduce() async {
  if (condition) {
    return await fetchData();
  }
  return state; // 这条路径没有await!
}

// 错误:调用异步函数但没有await
Future<AppState?> reduce() async {
  someAsyncFunction(); // 没有被等待
  return state;
}

使用assertUncompletedFuture()

对于具有多个代码路径的复杂reducer,在最终返回前添加assertUncompletedFuture()。这在开发时运行时捕获违规:

class ComplexAction extends ReduxAction<AppState> {
  @override
  Future<AppState?> reduce() async {
    if (state.cacheValid) {
      // 可能意外跳过await的复杂逻辑
      return processCache();
    }

    final data = await fetchFromServer();
    final processed = transform(data);

    assertUncompletedFuture(); // 验证至少发生了一次await
    return state.copy(data: processed);
  }
}

异步操作期间的状态更改

state getter可能在每次await后更改,因为其他action可能在您等待时修改状态:

class AsyncAction extends ReduxAction<AppState> {
  @override
  Future<AppState?> reduce() async {
    print(state.counter); // 例如,5

    await someSlowOperation();

    // state.counter现在可能不同了(例如,10)
    // 如果另一个action在await期间修改了它
    print(state.counter);

    return state.copy(counter: state.counter + 1);
  }
}

使用initialState进行比较

使用initialState访问action被分发时的状态(永远不会更改):

class SafeIncrement extends ReduxAction<AppState> {
  @override
  Future<AppState?> reduce() async {
    final originalCounter = initialState.counter;

    await validateWithServer();

    // 检查我们等待时状态是否更改
    if (state.counter != originalCounter) {
      // 状态被另一个action修改
      return null; // 中止我们的更改
    }

    return state.copy(counter: state.counter + 1);
  }
}

分发异步操作

即发即弃

当不需要等待完成时,使用dispatch()

context.dispatch(FetchUser());
// 立即返回,action在后台运行

等待完成

使用dispatchAndWait()来等待action的完成:

await context.dispatchAndWait(FetchUser());
// 仅在action完成且状态更改后继续
print('用户已加载: ${context.state.user.name}');

并行分发多个

// 分发所有,不等待
context.dispatchAll([FetchUser(), FetchSettings(), FetchNotifications()]);

// 分发所有并等待所有完成
await context.dispatchAndWaitAll([FetchUser(), FetchSettings()]);

显示加载状态

使用isWaiting()在异步操作运行时显示加载指示器:

Widget build(BuildContext context) {
  if (context.isWaiting(FetchUser)) return CircularProgressIndicator();  
  else return Text('你好, ${context.state.user.name}');
}

错误处理

抛出UserException用于面向用户的错误:

class FetchUser extends ReduxAction<AppState> {
  @override
  Future<AppState?> reduce() async {
    final response = await api.fetchUser();

    if (response.statusCode == 404) 
      throw UserException('用户未找到。');    

    if (response.statusCode != 200) 
      throw UserException('加载用户失败。请重试。');    

    return state.copy(user: response.data);
  }
}

在widget中检查失败:

Widget build(BuildContext context) {
  if (context.isFailed(FetchUser)) {
    return Text('错误: ${context.exceptionFor(FetchUser)?.message}');
  }
  // ...
}

完整示例

// 带有适当错误处理的异步操作
class LoadProducts extends ReduxAction<AppState> {
  @override
  Future<AppState?> reduce() async {
    try {
      final products = await api.fetchProducts();
      return state.copy(products: products, productsLoaded: true);
    } catch (e) {
      throw UserException('无法加载产品。请检查您的连接。');
    }
  }
}

// 显示所有三种状态的widget
Widget build(BuildContext context) {
  // 加载状态
  if (context.isWaiting(LoadProducts)) {
    return Center(child: CircularProgressIndicator());
  }

  // 错误状态
  if (context.isFailed(LoadProducts)) {
    return Center(
      child: Column(
        children: [
          Text(context.exceptionFor(LoadProducts)?.message ?? '错误'),
          ElevatedButton(
            onPressed: () => context.dispatch(LoadProducts()),
            child: Text('重试'),
          ),
        ],
      ),
    );
  }

  // 成功状态
  return ListView.builder(
    itemCount: context.state.products.length,
    itemBuilder: (_, i) => ProductTile(context.state.products[i]),
  );
}

返回类型警告

永远不要直接返回FutureOr<AppState?>。AsyncRedux必须知道action是同步还是异步:

// 正确
Future<AppState?> reduce() async { ... }

// 正确
AppState? reduce() { ... }

// 错误 - 抛出StoreException
FutureOr<AppState?> reduce() { ... }

参考

文档中的URL: