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.
Related
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);
I added the ORKOrderedTask.fitnessCheckTaskWithIdentifier Task and it renders find in the UI. But unlike other simpler tasks containing scale/choice/date questions, I was not able to find the exact way to read the sensor data collected via ORKOrderedTask.fitnessCheckTaskWithIdentifier.
I have used the following:
private var walkingTask : ORKTask {
return ORKOrderedTask.fitnessCheckTaskWithIdentifier("shortWalkTask", intendedUseDescription: "Take a short walk", walkDuration: 10, restDuration: 5, options: nil)
}
upon task completion the task view controller delegate below is hit.
//ORKTaskViewControllerDelegate
func taskViewController(taskViewController: ORKTaskViewController, didFinishWithReason reason: ORKTaskViewControllerFinishReason, error: NSError?)
is there a way to drill down into the result object contained in task view controller (taskViewController.result) to get the step count? Or will i have to go through health kit or something and then query the required observation? Request help from anyone who has used this task before and can provide some input on how to fetch the pedometer data (step count specifically) for the duration the task was active?
I'm using swift.
The step count is not reflected in the result objects per se. Instead, one of the child ORKFileResult objects, generated from the pedometer recorder, will contain the pedometer records queried from CoreMotion, serialized to JSON.
However, exposing the step count on a result object, sounds like a useful extension / improvement, and we should see if it generalizes to other recorders too. Please open an issue on GitHub and we will see what we can do!
I have created an observable by using Observable.FromEventPattern. Let's call it fromEvents.
I want to create another observable that wraps fromEvents. We'll call this 2nd observable wrapper.
When wrapper is subscribed to it should:
Publish the most recent item from fromEvents if any.
Publish the rest of items coming from fromEvents
Obviously wrapper will need to maintain a subscription to fromEvents so that it always has access to the most recent event.
I have tried various combinations of Replay, Publish, PublishLast, Observable.Defer and I'm never quite getting the results I'm looking for.
I'm certain Rx has operators that will meet my needs, I'm just unsure of exactly how to put everything together, being the newb that I am.
I think I've been able to get what I want by doing this:
Events = Observable.FromEventPattern(...).Replay(1).RefCount();
// contrived example
// in my real app the subscription lives for a specific duration
// and Events is exposed as a readonly property
using(Events.Subscribe())
{
// get most recent or wait for first
var e = Events.FirstAsync().Wait();
}
Example using the Publish overload that uses a BehaviorSubject behind the scenes to keep track of the most recent event.
var fromEvents = Observable.FromEventPattern(...);
var published = fromEvents.Publish(null);
// subscribe to this one
var wrapper = published.Where(e => e != null);
// start collecting values
var subscription = published.Connect();
wrapper.Subscribe(...);