Flutter Bloc异步函数调用emit触发断言错误问题排查
问题:Flutter Bloc中Timer周期性任务调用emit触发断言错误
你在Bloc的定时任务中调用emit时触发了_AssertionError,错误定位在periodicFetch方法的emit(WalletListState(wallets: wallets!))语句。
相关代码
Future<void> fetchInitialData(FetchInitialDataEvent event, Emitter<WalletState> emit) async { if (wallets != null) { emit(WalletListState(wallets: wallets!)); } else { walletsModel = await getWallets(); assetsModel = await getSupportedAssets(); cmc = await getCmc(); currencies = await getCurrencies(); fetchAddress(); wallets = walletEngine.buildWallet(assetsModel, walletsModel, cmc); if (wallets != null && configEngine.filteredMarkets == null && configEngine.orgMarkets != null) { configEngine.filteredMarkets = walletEngine.filterMarket(wallets!, configEngine.orgMarkets!); openSocket.init(configEngine.filteredMarkets!); } emit(WalletListState(wallets: wallets!)); } if (currencies != null) { listenTickerStream(currencies!, coins); } startWalletScheduler(emit); } startWalletScheduler(Emitter<WalletState> emit) { if (walletScheduler?.isActive != true) { Duration interval = Duration(seconds: walletIntervalSeconds); walletScheduler = Timer.periodic(interval, (timer) async { await periodicFetch(emit); }); } } Future<void> periodicFetch(Emitter<WalletState> emit) async { walletsModel = await getWallets(); wallets = walletEngine.buildWallet(assetsModel, walletsModel, cmc); emit(WalletListState(wallets: wallets!)); } Future<WalletsResponse?> getWallets() async { final result = await repo.wallets(cancelToken: cancelToken); return result.fold((error) { return Future.value(null); }, (resp) { return resp; }); }
错误栈
_AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61) I/flutter ( 8440): #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5) I/flutter ( 8440): #2 _Emitter.call (package:bloc/src/emitter.dart:114:7) I/flutter ( 8440): #3 WalletBloc.periodicFetch (package:drip_mobile/src/core/wallet/biz/wallet_bloc.dart:122:9) I/flutter ( 8440): #4 WalletBloc.startWalletScheduler.<anonymous closure> (package:drip_mobile/src/core/wallet/biz/wallet_bloc.dart:114:9)
问题原因
Bloc的Emitter实例是绑定到当前事件处理周期的,当fetchInitialData事件处理完成后,这个Emitter会被标记为无效。你把该Emitter传递给定时任务并在回调中使用,此时原事件周期已结束,调用emit就会触发断言错误。
解决方法
不要跨周期复用Emitter,而是通过Bloc的add方法发送新事件,让Bloc的标准事件处理流程来更新状态:
修改步骤
- 定义新事件用于触发钱包刷新:
class RefreshWalletsEvent extends WalletEvent {}
- 在Bloc中添加该事件的处理逻辑(以Bloc v8+的
on语法为例):
on<RefreshWalletsEvent>((event, emit) async { walletsModel = await getWallets(); wallets = walletEngine.buildWallet(assetsModel, walletsModel, cmc); emit(WalletListState(wallets: wallets!)); });
- 修改定时任务逻辑,改为发送事件而非直接调用emit:
void startWalletScheduler() { if (walletScheduler?.isActive != true) { Duration interval = Duration(seconds: walletIntervalSeconds); walletScheduler = Timer.periodic(interval, (timer) { add(RefreshWalletsEvent()); }); } }
- 调整
fetchInitialData中的调用,不再传递emit:
// 替换原startWalletScheduler(emit); startWalletScheduler();
异步函数中高效使用emit的建议
- 禁止跨事件周期复用Emitter:Emitter仅在当前事件处理的生命周期内有效,事件处理完成后必须丢弃。
- 通过事件触发状态更新:定时任务、异步回调等脱离当前事件周期的操作,一律通过
add方法发送新事件,让Bloc标准流程处理状态变更。 - 状态更新逻辑封装到事件处理中:所有emit操作应该放在事件处理回调内,保证状态流的可追踪性和一致性。
- 旧版Bloc使用async*流:如果使用Bloc v7及以下版本,在
mapEventToState中用async*配合yield发送状态,天然适配异步场景。
内容的提问来源于stack exchange,提问作者Mohammed Safiq
相关产品推荐
相关产品推荐

