RMK
RMK

Reputation: 1937

Flutter BLoC event race condition

Let's assume we have an app where user has a calendar where he can select a date for which he wants to get list of events. When user selects the date, we add an event CalendarSelectedDateChanged(DateTime). Bloc component fetches data from API each time we receive such an event. We can imagine a situation when the response is received with different delay. This will produce the following scenario:

Expected result is that we discard response from request date=1 here

How can we resolve such a race condition in the most elegant way (preferably for all BLoCs in the app at once)?

Here's an exemplary code that will produce such an issue:

class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {

  ExampleBloc()
      : super(ExampleDataLoadInProgress(DateTime.now())) {
    on<ExampleSelectedDateChanged>((event, emit) async {
      await _fetchData(event.date, emit);
    });
  }

  Future<void> _fetchData(DateTime selectedDate,
      Emitter<ExampleState> emit,) async {
    emit(ExampleDataLoadInProgress(selectedDateTime));
    try {
      final data = callAPI(selectedDateTime);
      emit(ExampleDataLoadSuccess(data, selectedDate));
    } on ApiException catch (e) {
      emit(ExampleDataLoadFailure(e, selectedDateTime));
    }
  }
}

Upvotes: 4

Views: 1970

Answers (1)

RMK
RMK

Reputation: 1937

By default all events are processed concurrently, you can change that behavior by setting a custom transformer:

You can read more about transformers here: https://pub.dev/packages/bloc_concurrency

class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {

  ExampleBloc()
      : super(ExampleDataLoadInProgress(DateTime.now())) {
    on<ExampleSelectedDateChanged>((event, emit) async {
      await _fetchData(event.date, emit);
    },
      transformer: restartable(), // ADD THIS LINE 
    );
  }

  Future<void> _fetchData(DateTime selectedDate,
      Emitter<ExampleState> emit,) async {
    emit(ExampleDataLoadInProgress(selectedDateTime));
    try {
      final data = callAPI(selectedDateTime);
      emit(ExampleDataLoadSuccess(data, selectedDate));
    } on ApiException catch (e) {
      emit(ExampleDataLoadFailure(e, selectedDateTime));
    }
  }
}

Upvotes: 6

Related Questions