I am trying to catch exceptions in mapEventToState in order to yield an ErrorState when one occurs. The code looks like this:
@override
Stream<MyState> mapEventToState(MyEvent event) async* {
try {
if (event is MyEvent) {
yield* _handleMyEvent();
}
} catch (e) {
print('in mapEventToState: $e');
yield ErrorState(message: e.message);
}
}
Stream<MyState> _handleMyEvent() async* {
try {
final id = await repo.getId();
} catch (e) {
print('in _handleMyEvent: $e');
rethrow;
}
}
When I run the code in a situation that will trigger the exception, I see the print from inside _handleMyEvent, but I do not see the one in mapEventToState, and the ErrorState is never yielded.
I can work around this by overriding the bloc's onError method:
@override
void onError(Object error, StackTrace stacktrace) {
add(InternalError(error: error));
}
And then just handling the InternalError event normally in mapEventToState. But it seems like it shouldn't be necessary. Perhaps it is related to throwing an Error in an async* method?
Hi @barapa 馃憢
Thanks for opening an issue!
If you have an async* (async-generator) then you should always handle exceptions internally and yield states. In your case I would recommend refactoring like:
@override
Stream<MyState> mapEventToState(MyEvent event) async* {
try {
if (event is MyEvent) {
yield await _handleMyEvent();
}
} catch (e) {
print('in mapEventToState: $e');
yield ErrorState(message: e.message);
}
}
Future<MyState> _handleMyEvent() async {
try {
final id = await repo.getId();
return MyState(id: id);
} catch (e) {
print('in _handleMyEvent: $e');
rethrow;
}
}
Let me know if that helps 馃憤
Closing for now but feel free to comment with additional questions and I'm happy to continue the conversation 馃憤
@felangel what if I want to throw exception for invalid states (so I want my app to crash for those states because the devs should prevent those from happening)? I want to test whether the exception was thrown. :S It should never occur in the actual app though.
@lunaticcoding you can just let the exception bubble up by not wrapping the code in a try/catch and then you can use blocTest with errors to verify an error occurred.
blocTest(
'CounterBloc throws Exception when null is added',
build: () async => CounterBloc(),
act: (bloc) => bloc.add(null),
errors: [
isA<Exception>(),
]
);
Hope that helps 馃憤
@felangel Thank you so much :) it is exactly what I was looking for :)
Most helpful comment
@felangel Thank you so much :) it is exactly what I was looking for :)