4

here is my use case:

I have two different Observables:

Observable<MobileAdEvent> adEventStream;
Observable<Color> colorStream;

I need to receive notification when either of those two emits an event. zipWith does not work for me, since it waits until both streams has an event to emit. mergeWith also does not seem to work, since it expects both stream to be from the same type. Any hints would be greatly appreciated, Thanks.

Angel Todorov
  • 1,443
  • 2
  • 19
  • 37

3 Answers3

6

What you want is combineLatest operator

Observable<Foo> foo;
Observable<Bar> bar;

final output = Observable.combineLatest2(foo, bar, (Foo foo, Bar bar) {
  // TODO: somehow merge them
});
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • 1
    Nope, it is not applicable - taken from the **observable.dart** file - **The Observable will not emit until all streams have emitted at least one item.**, which does not comply with my requirements above, since I need to have this one fired when **either** of the streams emits an event. – Angel Todorov Mar 21 '19 at 18:37
  • 1
    @AngelTodorov Just push a `null` on the stream – Rémi Rousselet Mar 21 '19 at 18:40
  • Yeah, that did the trick. I will mark it as an answer, however, it is a kind of a workaround - basically, I can use `zipWith` to achieve the very same result. I believe we need much cleaner method analogical to `mergeWith` but able to emit once either stream emits an event. – Angel Todorov Mar 21 '19 at 18:55
  • 2
    `zipWith` won't do. The combiner is called only after all streams submitted something. `combineLatest` combines the values after every single event (as long as there's an initial value). So `combineLatest` is much closer to `merge` – Rémi Rousselet Mar 21 '19 at 19:02
  • @RémiRousselet now with flutter null safety you can't push null in a non-null-emitting stream, then how can we apply this workaround now? – HII Oct 13 '21 at 14:49
2

Have you tried Observable.race ? it's perfect for notifications :

Given two or more source streams, emit all of the items from only the first of these streams to emit an item or notification.

new Observable.race([
  new Observable.timer(1, new Duration(days: 1)),
  new Observable.timer(2, new Duration(days: 2)),
  new Observable.timer(3, new Duration(seconds: 1))
]).listen(print); // prints 3

learn more here https://pub.dev/documentation/rxdart/latest/rx/Observable/Observable.race.html

jfab fab
  • 491
  • 1
  • 8
  • 19
  • It will give events only for "winner" stream (which made first event) and discard events from others. See visualization: https://rxmarbles.com/#race – Pavel Mar 31 '23 at 16:51
0

Use StreamGroup.merge and check object type:

import 'package:async/async.dart';

Stream<MobileAdEvent> adEventStream;
Stream<Color> colorStream;

final merged = StreamGroup.merge([
  _orderStream.stream,
  _orderStream.stream.map((event) => event.hashCode),
]);

final subscription = merged.listen((event) {
  if (event is MobileAdEvent) {
    // handle event from first stream
  }
  if (event is Color) {
    // handle event from second stream
  }
});
Pavel
  • 5,374
  • 4
  • 30
  • 55