I have two Observables. They are both of type Observable<T>.
One is a cold one called initialValueObservable which simply emits from a list of items by a Observable.from().
The other one is a hot one called valueUpdateObservable which is a PublishSubject that notifies the subscriber when there is a new item.
In the client I want to subscribe to both so I get the initial value from the initialValueObservable and the updates published by valueUpdateObservable.
My initial approach was to merge them but I think that won't work as the initialValueObservable will send onComplete at which point the new items emit by the valueUpdateObservable won't arrive.
When you do a merge the onComplete is only sent when all of the source observables, in this case both, complete.
Take this (c# Rx.NET) code as an example:
Observable
.Return(42L)
.Merge(Observable.Interval(TimeSpan.FromSeconds(1.0)).Take(3))
.Subscribe(x => Console.WriteLine(x));
It produces:
42
0
1
2
I'd be astounded if RxJava did anything different.
In case anyone comes looking for this, here is an example
Observable.concat(
coldObservable.doOnComplete{ () -> Log.d(TAG, "cold observable completed"); },
hotObservable.doOnComplete{ () -> Log.d(TAG, "hot observable completed") }
.subscribe()
Will Log all of the coldObservable's value followed by "cold observable completed" and then all of the hotObvservable's values. "hot observable completed" will not be Logged assuming it's something like a click listener that doesn't have a completed state
Related
I'm using a rxdart ZipStream within my app to combine two streams of incoming bluetooth data. Those streams are used along with "bufferCount" to collect 500 elements each before emitting. Everything works fine so far, but if the stream subscription gets cancelled at some point, there might be a number of elements in those buffers that are omitted after that. I could wait for a "buffer cycle" to complete before cancelling the stream subscription, but as this might take some time depending on the configured sample rate, I wonder if there is a solution to get those buffers as they are even if the number of elements might be less than 500.
Here is some simplified code for explanation:
subscription = ZipStream.zip2(
streamA.bufferCount(500),
streamB.bufferCount(500),
(streamABuffer, streamBBuffer) {
return ...;
},
).listen((data) {
...
});
Thanks in advance!
So for anyone wondering: As bufferCount is implemented with BufferCountStreamTransformer which extends BackpressureStreamTransformer, there is a dispatchOnClose property that defaults to true. That means if the underlying stream whose emitted elements are buffered is closed, then the remaining elements in that buffer are emitted finally. This also applies to the example above. My fault was to close the stream and to cancel the stream subscription instantly. With awaiting the stream's closing and cancelling the stream subscription afterwards, everything works as expected.
Problem
I am trying to load all of the data my application needs before using. Once I have all of the data I want to emit one event back to my subscription in my controller with either a success or failure status.
I am struggling to come up with a way to send an onComplete status on my observable chain so I can use toArray(). Maybe there's a way or just a better approach that I have not thought of.
Loading Workflow
Basically I have three different services: version, champion, and skin.
I retrieve the latest version and get all of the champions for that version. Then I download all of the images for each skin the champion has.
Observable Chain
My observable chain in my LoadingViewModel is something like this:
return versionService.getLatest().flatMap({ (version: VersionData) in
return championService.getChampions(forVersion: version)
}).flatMap({ (champions: [Champion]) -> Observable<Champion> in
// Go through champions and update total skin count
// emit each champion
}).flatMap({ (champion: Champion) -> Observable<Skin> in
return skinService.getSkin(forChampion: champion)
}).toArray() // Doesn't work since I don't send complete
.flatMap({ (result: [Skin]) -> Observable<LoadingViewModelResult> in
return Observable.just(LoadingViewModelResult.success)
})
If I don't have the toArray() there then I emit a LoadingViewModelResult.success for each skin which floods my view controller with an abundance of unwanted successful results.
What I've Tried
I attempted to use .take(count) where I passed in the skin count but since it is initialized to 0 the chain will instantly take 0 and return.
For my [Champion] -> Observable<Champion> and Champion -> Observable<Skin> observables I needed to include an observer.onCompleted within the creation of the Observable.
I have an observable object and I want to emit a default element after some time has passed (a timeout) while keeping the stream still open to emit new values in the future. How can I do this?
I attempted to do this by doing a merge of the original stream with another stream that debounces the original one while mapping the debounced value to the default that I want.
Pseudocode:
defaultDebounced = originalStream.debounce(time).map({x -> myDefaultValue})
myStream = rx.merge(originalStream, defaultDebounced)
though I don't know if I would run into some border cases like the following in which the original stream emits an item just as the timeout triggers and by chance, the default value gets emitted afterwards.
original: ----A----B----------------------C------------
debounced: -----------------------<timeout>X------------
merged: --------------------------------CX-----------
Also, there's the downside that the first observable has to emit at least one item in order for debounce to emit the default value.
Note: I would like to know the proper rx way to do it, regardless of the implementation, but just in case I'm working on RxSwift.
What I finally did was:
originalStream.flatMapLatest({x ->
return Observable.timer(30, scheduler: MainScheduler.instance)
.map{_ -> defaultValue}
.startWith(x)
})
I'm confused by the behavior of a shared stream that is created using Rx.Observable.just.
For example:
var log = function(x) { console.log(x); };
var cold = Rx.Observable
.just({ foo: 'cold' });
cold.subscribe(log); // <-- Logs three times
cold.subscribe(log);
cold.subscribe(log);
var coldShare = Rx.Observable
.just({ foo: 'cold share' })
.share();
coldShare.subscribe(log); // <-- Only logs once
coldShare.subscribe(log);
coldShare.subscribe(log);
Both streams only emit one event, but the un-shared one can be subscribed to three times. Why is this?
I need to "fork" a stream but share its value (and then combine the forked streams).
How can I share the value of a stream but also subscribe to it multiple times?
I realize that this is probably related to the concept of "cold" and "hot" observables. However:
Is the stream created by Rx.Observable.just() cold or hot?
How is one supposed to determine the answer to the previous question?
Is the stream created by Rx.Observable.just() cold or hot?
Cold.
How is one supposed to determine the answer to the previous question?
I guess the documentation is the only guide.
How can I share the value of a stream but also subscribe to it multiple times?
You are looking for the idea of a connectable observable. By example:
var log = function(x) { console.log(x); };
var coldShare = Rx.Observable
.just({ foo: 'cold share' })
.publish();
coldShare.subscribe(log); // Does nothing
coldShare.subscribe(log); // Does nothing
coldShare.subscribe(log); // Does nothing
coldShare.connect(); // Emits one value to its three subscribers (logs three times)
var log = function(x) {
document.write(JSON.stringify(x));
document.write("<br>");
};
var coldShare = Rx.Observable
.just({ foo: 'cold share' })
.publish();
coldShare.subscribe(log); // <-- Only logs once
coldShare.subscribe(log);
coldShare.subscribe(log);
coldShare.connect();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.7/rx.all.min.js"></script>
The example above logs three times. Using publish and connect, you essentially "pause" the observable until the call to connect.
See also:
How do I share an observable with publish and connect?
Are there 'hot' and 'cold' operators?
I don-t understand your first question, but about the last one, as I have been having problem getting that one too:
Rxjs implementation of Observables/Observers is based on the observer pattern, which is similar to the good old callback mechanism.
To exemplify, here is the basic form of creating an observable (taken from the doc at https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/create.md)
var source = Rx.Observable.create(function (observer) {
observer.onNext(42);
observer.onCompleted();
// Note that this is optional, you do not have to return this if you require no cleanup
return function () {
console.log('disposed');
};
});
Rx.Observable.create takes as argument a function (say factory_fn to be original) which takes an observer. Your values are generated by a computation of your choice in the body of factory_fn, and because you have the observer in parameter you can process/push the generated values when you see fit. BUT factory_fn is not executed, it is just registered (like a callback would). It will be called everytime there is a subscribe(observer) on the related observable (i.e. the one returned by Rx.Observable.create(factory_fn).
Once subscription is done (creation callback called), values flow to your observer according to the logic in the factory function and it remains that way till your observable completes or the observer unsubscribes (supposing you did implement an action to cancel value flow as the return value of factory_fn).
What that basically means is by default, Rx.Observables are cold.
My conclusion after using quite a bit of the library, is that unless it is duely documented, the only way to know FOR SURE the temperature of an observable is to eye the source code. Or add a side effect somewhere, subscribe twice and see if the side effect happens twice or only once (which is what you did). That, or ask on stackoverflow.
For instance, Rx.fromEvent produce hot observables, as you can see from the last line in the code (return new EventObservable(element, eventName, selector).publish().refCount();). (code here : https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/linq/observable/fromevent.js). The publish operator is among those operators which turns a cold observable into a hot one. How that works is out of scope so I won-t detail it here.
But Rx.DOM.fromWebSocket does not produce hot observables (https://github.com/Reactive-Extensions/RxJS-DOM/blob/master/src/dom/websocket.js). Cf. How to buffer stream using fromWebSocket Subject
Confusion often comes I think from the fact that we conflate the actual source (say stream of button clicks) and its representation (Rx.Observable). It is unfortunate when that happens but what we imagine as hot sources can end up being represented by a cold Rx.Observable.
So, yes, Rx.Observable.just creates cold observables.
I have a push notification mechanism (PublishSubject) which triggers http request logic (flatMap). Basic scenario is that whenever a push arrives, single http call is made and results propagated to multiple observers.
I've written a simple demo for the case but flatMap executes for each registered observer, while I would like it to be triggered just once on each push.
PublishSubject<Integer> subject = PublishSubject.create();
Observable<String> obs = subject.asObservable().flatMap(integer -> {
// this code runs for each observer, which is twice in this case
return Observable.just(String.valueOf(integer));
});
Observer mock = mock(Observer.class);
Observer mock1 = mock(Observer.class);
obs.subscribe(mock);
obs.subscribe(mock1);
subject.onNext(1);
Could you suggest a fix?
Thanks
P.S. Right now I'm using cache(1) to fix the issue but I'm not sure if it's ok to do it this way. Moreover, I can't just quite understand why a single stream of execution would depend on amount of observers attached. Can you comment on that?
You are already sort of using it, you need either publish() + connect() or publish().refCount() if you want to make the value shareable by multiple observables. The first case lets you control when to actually make the Observable go hot, while the second will go live as soon as you subscribe the first time. RxJS also has share which wraps publish().refCount() not sure if RxJava has that as well.
PublishSubject<Integer> subject = PublishSubject.create();
Observable<String> obs = subject.asObservable().flatMap(integer -> {
// this code runs for each observer, which is twice in this case
return Observable.just(String.valueOf(integer));
}).publish().refCount();
Observer mock = mock(Observer.class);
Observer mock1 = mock(Observer.class);
obs.subscribe(mock);
obs.subscribe(mock1);
subject.onNext(1);