Bloc: Q: how to combine await and yield in foreach

Created on 21 Aug 2019  路  6Comments  路  Source: felangel/bloc

sorry, this is not a issue or feature request, it's just a question

i have something like this in my bloc :

yield Loading();
final factors = []; // read some objects from db
await Future.forEach<FactorF>(factors, (factor) async* {
    yield UpdateLoading(factor.id, true, false);
    final result = await factor.send();
    yield UpdateLoading(factor.id, false, result);
});
yield Finish();

i want send one by one factors and updating status, for using yield inside Future.forEach i must use async*, but when i using this instead of async, callback method not called

how can doing it ?
thanks

question

Most helpful comment

@DJafari sorry for the delay but if you refactor mapEventToState to this it should work

  @override
  Stream<MyState> mapEventToState(MyEvent event) async* {
    if (event is TestEvent) {
      final factors = [1, 2, 3, 4];
      await for (int num in Stream.fromIterable(factors)) {
        yield UpdateLoading(num, true, false);
        await Future.delayed(Duration(seconds: 3));
        yield UpdateLoading(num, false, false);
      }
      yield Finish();
    }
  }

All 6 comments

Hi @DJafari 馃憢
Thanks for opening an issue!

Regarding your question, you need to use yield* (yield-each) in order to push the new data onto the bloc state sink.

yield Loading();
final factors = []; // read some objects from db
yield* await Future.forEach<FactorF>(factors, (factor) async* {
    yield UpdateLoading(factor.id, true, false);
    final result = await factor.send();
    yield UpdateLoading(factor.id, false, result);
});
yield Finish();

Hope that helps 馃憤

@felangel thanks man

when i use your solution, i get this exception :

NoSuchMethodError: The method 'listen' was called on null.
    Receiver: null
    Tried calling: listen(Closure: (Object) => void from Function '_add@8048458':., cancelOnError: false, onDone: Closure: () => void from Function '_close@8048458':., onError: Closure: (Object, StackTrace) => void from Function '_addError@8048458':.)

@felangel for saving your time
this is sample code for reproduced this exception :

import 'package:equatable/equatable.dart';
import 'package:bloc/bloc.dart';

abstract class MyState extends Equatable {
  MyState([List props = const []]) : super(props);
}
class Init extends MyState {
  @override
  String toString() => "Init";
}
class Loading extends MyState {
  @override
  String toString() => "Loading";
}
class UpdateLoading extends MyState {
  final int factorId;
  final bool status;
  final bool result;

  UpdateLoading(this.factorId, this.status, this.result) : super([factorId, status, result]);

  @override
  String toString() => "UpdateLoading: { factorId: $factorId, status: $status, result: $result }";
}
class Finish extends MyState {
  @override
  String toString() => "Finish";
}

abstract class MyEvent extends Equatable {
  MyEvent([List props = const []]) : super(props);
}

class TestEvent extends MyEvent {
  @override
  String toString() => "TestEvent";
}

class MyBloc extends Bloc<MyEvent, MyState> {
  @override
  MyState get initialState => Init();

  @override
  Stream<MyState> mapEventToState(MyEvent event) async* {
    if(event is TestEvent) {
      final factors = [1,2,3,4];
      yield* await Future.forEach<int>(factors, (num) async* {
        yield UpdateLoading(num, true, false);
        await Future.delayed(Duration(seconds: 3));
        yield UpdateLoading(num, false, false);
      });
      yield Finish();
    }
  }
}

@DJafari sorry for the delay but if you refactor mapEventToState to this it should work

  @override
  Stream<MyState> mapEventToState(MyEvent event) async* {
    if (event is TestEvent) {
      final factors = [1, 2, 3, 4];
      await for (int num in Stream.fromIterable(factors)) {
        yield UpdateLoading(num, true, false);
        await Future.delayed(Duration(seconds: 3));
        yield UpdateLoading(num, false, false);
      }
      yield Finish();
    }
  }

@felangel I met this issue recently, and await for is blocking bloc to receive new event. What I tried to achieve is to stop action immediately! Stop current uploading progress, but the bloc would not receive the event when await for is doing the jobs. Is there any way to slove this problem?

The only workaround I tried is to use listen instead of await for without blocking the code, and make a log event to dispatch in the callback instead of yield the log state in await for. I'm not sure If this is the only way to do so. 馃槩

Hi @Tokenyet 馃憢

If you want to stop processing the current event you can override transformEvents and use switchMap. Check out https://github.com/felangel/bloc/issues/517 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

clicksocial picture clicksocial  路  3Comments

MahdiPishguy picture MahdiPishguy  路  3Comments

krusek picture krusek  路  3Comments

nhwilly picture nhwilly  路  3Comments

rsnider19 picture rsnider19  路  3Comments