You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Riverpod AsyncNotifier build方法异常捕获优化可行性咨询

问题描述

我在Riverpod的MyServiceController(继承自_$MyServiceController的AsyncNotifier)的build方法中,原本用try/catch捕获异常并返回null来避免页面卡顿;同时通过ProviderScope在特定页面覆盖该Provider,切换页面时build方法会自动触发。

当前代码:

@riverpod
class MyServiceController extends _$MyServiceController {
  static final Logger _logger = Logger('MyServiceController');
  @override
  FutureOr<bool?> build() async {
    _logger.info('MyServiceController.build');

    ref.onDispose(() {
      _logger.info('MyServiceController.onDispose');
    });
    try {
      final currentAccount = await ref.read(authRepoProvider).currentUser;
      final rep = await ref
          .read(servicesProvider)
          .hasService(userId: currentAccount.$id);
      return rep;
    } catch (e) {
      return null;
    }
  }
}

覆盖Provider的代码:

ProviderScope(
  overrides: [
    myServiceControllerProvider.overrideWith(MyServiceController.new)
  ],
  // 子页面内容
);

现在我希望:

  1. 切换页面时该Provider的build方法仍能触发;
  2. 移除try/catch,改为手动管理状态,让错误发生时Provider的状态变为错误态而非返回null,修改后的代码雏形如下:
@override
FutureOr<bool?> build() async {
  _logger.info('MyServiceController.build');
  state = const AsyncLoading(); //added

  ref.onDispose(() {
    _logger.info('MyServiceController.onDispose');
  });
  try {
    final currentAccount = await ref.read(authRepoProvider).currentUser;
    final rep = await ref
        .read(servicesProvider)
        .hasService(userId: currentAccount.$id);
    state = AsyncValue.data(rep);//added
    return rep;
  } catch (e,stacktrace) {
    state = AsyncValue.error(e,stacktrace);//added
    return null;
  }
}

核心疑问:是否可以删除try/catch来实现上述需求?


解答

可以删除try/catch,但要调整状态管理逻辑

Riverpod的AsyncNotifier原生就会自动捕获build方法中的异步异常,并将状态转为AsyncError,完全不需要手动用try/catch包裹。不过删除try/catch后,要注意以下两点来满足你的需求:

  1. 页面切换时build方法触发的逻辑不受影响
    你目前用ProviderScope覆盖Provider的方式是正确的:每次进入目标页面时,会创建新的MyServiceController实例,触发build;离开页面时实例销毁,再次进入会重新初始化,这个逻辑和try/catch无关,保持现有覆盖代码即可。

  2. 状态会被自动管理(加载、成功、错误)
    不需要手动设置state = const AsyncLoading()——AsyncNotifier在build开始执行时,会自动将状态切换为加载态。你只需要在build中正常返回异步操作的结果,一旦发生异常,AsyncNotifier会自动捕获并将状态转为AsyncError,完全符合你的需求。

优化后的最终代码

@riverpod
class MyServiceController extends _$MyServiceController {
  static final Logger _logger = Logger('MyServiceController');

  @override
  FutureOr<bool?> build() async {
    _logger.info('MyServiceController.build');

    ref.onDispose(() {
      _logger.info('MyServiceController.onDispose');
    });

    // AsyncNotifier会自动处理加载状态
    final currentAccount = await ref.read(authRepoProvider).currentUser;
    final rep = await ref
        .read(servicesProvider)
        .hasService(userId: currentAccount.$id);
    return rep;
    // 异常会被自动捕获,state转为AsyncError
  }
}

自定义错误处理的补充

如果需要在异常发生时做额外操作(比如日志上报),可以保留try/catch,但必须重新抛出异常,让AsyncNotifier继续处理状态转换:

@override
FutureOr<bool?> build() async {
  _logger.info('MyServiceController.build');

  ref.onDispose(() {
    _logger.info('MyServiceController.onDispose');
  });

  try {
    final currentAccount = await ref.read(authRepoProvider).currentUser;
    final rep = await ref
        .read(servicesProvider)
        .hasService(userId: currentAccount.$id);
    return rep;
  } catch (e, stacktrace) {
    _logger.severe('获取服务状态失败', e, stacktrace);
    rethrow; // 重新抛出,让AsyncNotifier转为错误状态
  }
}

内容的提问来源于stack exchange,提问作者Mosh Mobile Software Engineer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 05:40:31