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) ], // 子页面内容 );
现在我希望:
- 切换页面时该Provider的
build方法仍能触发; - 移除
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后,要注意以下两点来满足你的需求:
页面切换时
build方法触发的逻辑不受影响
你目前用ProviderScope覆盖Provider的方式是正确的:每次进入目标页面时,会创建新的MyServiceController实例,触发build;离开页面时实例销毁,再次进入会重新初始化,这个逻辑和try/catch无关,保持现有覆盖代码即可。状态会被自动管理(加载、成功、错误)
不需要手动设置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

