Bloc: Cannot catch exceptions in mapEventToState

Created on 28 Jan 2020  路  6Comments  路  Source: felangel/bloc

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.

question

Most helpful comment

@felangel Thank you so much :) it is exactly what I was looking for :)

All 6 comments

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.

https://stackoverflow.com/questions/62626632/bloc-handleerror-does-not-catch-error-in-dart?noredirect=1#comment110750993_62626632

@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 :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shawnchan2014 picture shawnchan2014  路  3Comments

RobPFarley picture RobPFarley  路  3Comments

clicksocial picture clicksocial  路  3Comments

frankrod picture frankrod  路  3Comments

Reidond picture Reidond  路  3Comments