How to properly consume this Stream method from Bloc to emit states - flutter

I am trying to process a method of type Stream from a Bloc implementation, but it seems I am not properly handling concurrency and I would need some assistance.
I have the following interface and implementations to wrap data
abstract class ResultWrapper {}
class ResultStart extends ResultWrapper {}
class ResultFetching extends ResultWrapper {}
class ResultDone extends ResultWrapper {}
then I have the following method to emit above classes in a stream fashion way
class FetchCountries {
Stream<ResultWrapper> fetch() async* {
yield ResultStart();
print('starting request');
yield ResultFetching();
print('fetching');
yield ResultDone();
print('done');
}
}
and finally, this is my Bloc implementation where I try to orchestrate my Event with States
CountryBloc(this.fetchCountries): super(InitialState()) {
on<FetchCountriesEvent>((event, emit) async {
fetchCountries.fetch().listen((result) {
if (result is ResultStart) {
emit(LoadingState());
} else if (result is ResultFetching) {
emit(AllFetchedState());
} else if (result is ResultDone) {
emit(DoneState());
}
});
});
}
but it seems I am either not listening the stream method properly or emiting States properly because I get following exception
Exception has occurred. _AssertionError ('package:bloc/src/bloc.dart':
Failed assertion: line 137 pos 7: '!_isCompleted': emit was called
after an event handler completed normally. This is usually due to an
unawaited future in an event handler. Please make sure to await all
asynchronous operations with event handlers and use emit.isDone after
asynchronous operations before calling emit() to ensure the event
handler has not completed.
I searched about that stacktrace but almost all suggestions I found are related to Future methods instead Stream methods and I was really not able to find a solution for my use case.
Can someone please tell me how to properly consume this Stream method from Bloc to emit states?
Thank you so much in advance.

You should use await emit.forEach for stream.
It will be like
await emit.forEach(
yourStream,
onData: (data) {
return yourState;
},
)

Related

Flutter tests throw "Bad state: Future already completed" when testing a call to `notifyListeners`

I am trying to test a class with a function like that
class A {
void doStuff() {
// Do stuff...
notifyListeners();
}
}
I am currently using the flutter-test package to automate my tests.
My test looks like this:
void main() {
final myInstance = A();
group("Class A", () {
test("should correctly do stuff", () async {
myInstance.doStuff();
expect(...);
});
});
}
The test is working and expect yields the correct result.
However, console shows an error message:
The following StateError was thrown while dispatching notifications for Class A:
Bad state: Future already completed
What causes this error and how can I prevent it from happening?
Turns out, notifyListeners() was the root-cause here.
It threw an error, presumably because it was not yet initialized.
By waiting for its initialization, the error has stopped occuring.
See TestWidgetsFlutterBinding.ensureInitialized for reference
setUpAll(() {
TestWidgetsFlutterBinding.ensureInitialized();
});

does garbage collector closes the streamController when reference is lost?

I am very confused about how actually the StreamController is implemented in dart.
So tell me if this code cause any memory leaks.
class Backend{
final streams = <int,StreamController>{};
final ws;
StreamHandler(this.ws){
listenToWebSocket();
}
listenToWebSocket(){
ws.listen((e){
streams[e['index']].add(e['data']);
});
}
getStream(int index){
var s = StreamController();
streams[index] = s;
return s.stream;
}
}
Backend listens to the websocket and passes its data to the stream appropriate index.
class StreamListenerHandler{
Map<int,StreamListener> listeners = {};
addListener(int index){
map[index] = listeners;
}
}
class StreamListener{
final int index;
StreamListener(this.index){
startListening();
}
startListening(){
getIt<Backend>().getStream().listen((data){
//do stuff
});
}
}
StreamListener wants the websocket data. StreamListenerHandler stores the listeners.
(for those who don't know getIt package just accesses the global singleton type thing.)
so what happens if...
void main(){
var handler = StreamListenerHandler();
handler.addListener(5);
// after some time...
handler.addListener(5);
}
here on the first call of addListener a StreamListener is created and it recieves a fresh stream from the Backend.
here is my expectation about the second call...
the StreamListenerHandler replaces the current StreamListener with a new one. Then the new StreamListener calls Backend and gets a fresh stream. The old streamController has no references so it's disposed. For the old StreamListener, the reference in the StreamListenerHandler is lost and also as the old streamController is gone the listen callback is also worthless, so it will be disposed.
So I am closing nothing just removing the references. Will this cause memory wastage?
does the garbage collector calls close on the controller or just disposing the object is enough?
(I am asking all this because all over the internet people are saying you should close the streams. I don't like the "should". I want to do it only if it "required")

Why to mark unit test as async in Flutter

I am newbie in Flutter as well as TDD and I do not understand why and when to mark unit test as async in flutter.
Looking through the documentation I found this code snippet:
// Create a MockClient using the Mock class provided by the Mockito package.
// Create new instances of this class in each test.
class MockClient extends Mock implements http.Client {}
main() {
group('fetchPost', () {
test('returns a Post if the http call completes successfully', () async {
final client = MockClient();
// Use Mockito to return a successful response when it calls the
// provided http.Client.
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('{"title": "Test"}', 200));
expect(await fetchPost(client), const TypeMatcher<Post>());
});
test('throws an exception if the http call completes with an error', () {
final client = MockClient();
// Use Mockito to return an unsuccessful response when it calls the
// provided http.Client.
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('Not Found', 404));
expect(fetchPost(client), throwsException);
});
});
}
If you look carefully you will noticed that first test is marked as async and the second is not. Why is that? What is different between these two test(except the cases) so that the first one has to be async?
Thanks :)
When you want to use await, you have to mark a callback or function in general as async.
In your case:
expect(await fetchPost(client), const TypeMatcher<Post>());
The await is needed because the result of the function execution matters. They are expecting exactly a Post type to be returned, hence, they need the await.
In the other case:
expect(fetchPost(client), throwsException);
It only matters that an exception is thrown, but the result is irrelevant.
When to mark callback with async when testing
Whenever you need await, you mark your callbacks with async. In general, I would advise always awaiting functions in tests because the tests will otherwise run in parallel, which could show undesired behavior.

AndroidSchedulers.mainThread observes onError earlier than onNext [duplicate]

I'm using RxJava in and Android application with RxAndroid. I'm using mergeDelayError to combine two retro fit network calls into one observable which will process emitted items if either emits one and the error if either has one. This is not working and it is only firing off the onError action when either encounters an error. Now to test this I shifted to a very simple example and still the successAction is never called when I have an onError call. See example below.
Observable.mergeDelayError(
Observable.error(new RuntimeException()),
Observable.just("Hello")
)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.finallyDo(completeAction)
.subscribe(successAction, errorAction);
The success action will only be called if I use two success observables. Am I missing something with how mergeDelayError is supposed to work?
EDIT:
I've found that if I remove the observeOn and subscribeOn everything works as expected. I need to specify threads and thought that was the whole point of using Rx. Any idea why specifying those Schedulers would break the behavior?
Use .observeOn(AndroidSchedulers.mainThread(), true) instead of .observeOn(AndroidSchedulers.mainThread()
public final Observable<T> observeOn(Scheduler scheduler, boolean delayError) {
return observeOn(scheduler, delayError, RxRingBuffer.SIZE);
}
Above is the signature of observeOn function. Following code works.
Observable.mergeDelayError(
Observable.error(new RuntimeException()),
Observable.just("Hello")
)
.observeOn(AndroidSchedulers.mainThread(), true)
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
}
});
Got this trick from ConcatDelayError thread: https://github.com/ReactiveX/RxJava/issues/3908#issuecomment-217999009
This still seems like a bug in the mergeDelayError operator but I was able to get it working by duplicating the observerOn and Subscribe on for each observable.
Observable.mergeDelayError(
Observable.error(new RuntimeException())
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()),
Observable.just("Hello")
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
)
.finallyDo(completeAction)
.subscribe(successAction, errorAction);
I think you don't wait for the terminal event and the main thread quits before the events are delivered to your observer. The following test passes for me with RxJava 1.0.14:
#Test
public void errorDelayed() {
TestSubscriber<Object> ts = TestSubscriber.create();
Observable.mergeDelayError(
Observable.error(new RuntimeException()),
Observable.just("Hello")
)
.subscribeOn(Schedulers.io()).subscribe(ts);
ts.awaitTerminalEvent();
ts.assertError(RuntimeException.class);
ts.assertValue("Hello");
}

Rxjava2, zipped iterable and interval, executes only a single mapped observable

I have this the following scenario I need to achieve:
perform each network call for a list of request object with 1 second delay each
and I have this following implementation using rxjava2
emit an interval stream
emit an iterable stream
zip them to emit each item from the iterable source
which by far has no problem and I fully understand how it works, now I integrated the above to the following
map each item emitted from zip into a new observable that defer/postpone an observable source for a network call
each mapped-emitted observable will perform an individual network call for each request
which I ended up with the following code
Observable
.zip(Observable.interval(1, TimeUnit.SECONDS), Observable.fromIterable(iterableRequests), new BiFunction<Long, RequestInput, RequestResult>() {
#Override
public RequestResult apply(#NonNull Long aLong, #NonNull final RequestInput request) throws Exception {
return request;
}
})
.map(new Function<RequestResult, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(#NonNull RequestResult requestResult) throws Exception {
// map each requestResult into this observable and perform a new stream
return Observable
.defer(new Callable<ObservableSource<?>>() {
// return a postponed observable for each subscriber
})
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
// return throwable observable
})
}
})
.subscribe(new Observer<ObservableSource<?>>() {
//.. onSubscribe {}
//.. onError {}
//.. onComplete {}
#Override
public void onNext(ObservableSource<?> observableSource) {
// actual subscription for each of the Observable.defer inside
// so it will start to emit and perform the necessary operation
}
});
but the problem is, it executes the Observable.defer source, only ONCE, but keeps on iterating(by putting a Log inside the map operator to see the iteration).
Can anyone guide me please on how can I achieve what I want, I exhausted alot of papers, drawing alot of marble diagrams, just to see where Im at on my code,
I dont know if the diagram I created illustrate the thing that I want, if it does, I dont know why does the sample code dont perform as the diagram portraits
Any help would be greatly appreciated.
The first part is fine, but the map thingy is a bit unneeded, what you are doing is mapping each RequestResult to an Observable, and then manually subscribe to it at the Observer.onNext(), actually the defer is not necessary as you're creating separate Observable for each RequestResult with different data, defer will occur at each subscribe yoy do at onNext(), and the map occur as you observed for each emission of the zipped RequestResult.
what you probably need is simple flatMap() to map each RequestResult value to a separate Observable that will do the network request, and it will merge back the result for each request to the stream, so you'll just need to handle the final values emission for each request instead to subscribe manually to each Observable.
Just keep in mind that order might be lost, in case some requests might take longer than your delay between them.
Observable.zip(Observable.interval(1, TimeUnit.SECONDS), Observable.fromIterable(iterableRequests),
new BiFunction<Long, RequestInput, RequestResult>() {
#Override
public RequestResult apply(#NonNull Long aLong,
#NonNull final RequestInput request) throws Exception {
return request;
}
})
.flatMap(new Function<RequestResult, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(RequestResult requestResult) throws Exception {
return createObservableFromRequest(requestResult)
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
// return throwable observable
})
}
})
.subscribe(new Observer<ObservableSource<?>>() {
//.. onSubscribe {}
//.. onError {}
//.. onComplete {}
#Override
public void onNext(ObservableSource<?> observableSource) {
//do something with each network result request emission
}
});
I manage to make it work, as somewhere inside the Observable.defer, my retrofitclient was null,
retrofitClient.getApiURL().post(request); // client was null
my retrofitClient was null ( i looked somewhere in the code and I noticed i was not initialized, and I initialized it properly and made it work)
now can anybody tell me why Rx didnt throw an exception back to the original observable stream? theres no NullPointerException that occurred, Im confused