RxSwift: how to have one observable trigger another one? - rx-swift

Suppose I have observable A, and I am trying to create observable B that emits two events: the first when A emits an event, and the second 5 seconds later.
So far I have the following:
self.B = Observable.create { [unowned self] observer in
self.A.subscribe(onNext: {
observer.onNext(0)
self.delay(5) {
observer.onNext(1)
}
})
return Disposables.create()
}
This works, but I feel uncomforatble subscribing to A from a closure. Is there a nicer way to do it?
Thanks!

The solution is to reuse the a observable for the delayed observable. Below is the code to do it, along with a proof of concept.
let a = button.rx.tap.asObservable()
let delay = a.delay(5.0, scheduler: MainScheduler.instance)
let b = Observable.of(a, delay).merge()
b.subscribe(onNext: {
print("foo")
}).disposed(by: bag)

Related

How Can I use the Bind Method in RXSwift?

The following sample code throws an error stating
No exact matches in call to instance method 'bind'
How can I bind the onNext of my publish subject to my observable?
let publish = PublishSubject<Void>()
// Error Here
publish.bind(to: myMethod())
func myMethod() -> Observable<Void> {
return Observable.create{ observer in
observer.onNext(())
observer.onCompleted()
return Disposables.create()
}
}
So what I want is everytime my publish subject emits an onNext event I want to trigger the observable of 'myMethod'
I'm not sure how to interpret this, but it sounds like you want something like:
let publish = PublishSubject<Void>()
let response = publish.flatMap { myMethod() }
response
.bind(onNext: { print($0) })
func myMethod() -> Observable<Void> {
Observable.create{ observer in
observer.onNext(())
observer.onCompleted()
return Disposables.create()
}
}
But it all seems rather pointless since all myMethod() does is emit a next event.
First, the bind(to:) method is in the RxCocoa Framework so you need to add import RxCocoa in your file.
Then, the observable created by myMethod will only be able to emit the event from the .create block. You can't use bind on it. If you need an observable with both the events from your subject and from the myMethod observable, you can do that :
let thirdObservable = Observable.merge(publish, myMethod())

RxJS Unsubscribe Only From Inner Observable

Let's say I have an interval that each second sends an heartbeat. At each beat i'd like to inspect something on my web page and react accordingly. I'd also like the option to unsubscribe from the inner Observables actions, but keep getting the heartbeat so when i subscribe back, everything will flow as before.
Creating a Subscription from Interval and piping it leaves no option to unsubscribe from the inner action, but only the whole subscription as whole.
Is there a way to return the inner Observable so i can unsubscribe from it while still retaining the heartbeat created from the Interval?
Edit: I've tried to create a class to describe what I'm talking about:
class Monitor {
sub: Subscription | null = null;
start() {
this.sub = this.monitor().subscribe();
}
monitor() {
const dom$ = someSelectorObserver(this.win.document, '#someSelector').pipe(
mergeMap(newElementOrBail => {
if (newElementOrBail) {
return handle(newElementOrBail);
} else {
return bail();
}
}),
tap({
error: error => this.log.error(error),
}),
);
return dom$;
}
handle(ele: HTMLElement) {
// do stuff
}
bail() {
this.sub.unsubscribe();
}
}
So basically my monitor starts with creating the subscription, as long as there's a new element to handle everything is fine, but when a bail signal appears I'd like to unsubscribe while still monitoring the DOM changes for a return of the previous elements.
So the outer subscription is basically the DOM observer and the inner is the mergeMap handle function. Does it make more sense?
You could just put some conditional on your inner observable:
private takeSignal = true
interval(3000).pipe(switchMap(() => takeSignal ? inner$ : NEVER))
Then just flip takeSignal as needed.
But it seems easier to just unsubscribe from the whole thing and resubscribe when needed. Why keep the interval going when you’re not using it?
You can split your logic in two (or more) streams.
Store heartbeat$ in a separate variable and subscribe to multiple times for different reasons.
In this way, you'd be able to split your logic into different streams and control subscriptions individually.
const heartbeat$ = interval(3000);
const inspectWeb = heartbeat$.pipe(
// do stuff
).subscribe()
inspectWeb.unsubscribe()
heartbeat$.pipe(
// do other stuff
).subscribe()

Signal when a ReplaySubject has "finished"

I am trying to figure out a good way to signal that a ReplaySubject is currently "empty".
import {ReplaySubject} from 'rxjs/ReplaySubject';
const rs = new ReplaySubject<Object>();
// ...
constructor(){
this.sub = rs.subscribe(...);
}
everytime the constructor is called, it will replay all the items from the subject. However my question is - is there some event we can listen for, that tells us when the subject becomes empty?
The only thing that I can think of is to fire a custom/different event when subject is done, something like this:
rs.next({done:true});
is passing data to the next() method the best way to signal that ReplaySubject is (temporarily) empty/out of events?
Because Subject is also an Observer you can call complete() after the final next() call. Then you can listen it with 3rd parameter of subscribe(...) method.
const rs = new ReplaySubject<Object>();
rs.next({a: 1})
rs.complete()
rs.subscribe(
obj => console.log(obj),
error => {},
() => console.log('completed')
);
// Outputs
// {a: 1} completed
Well now, I suppose you could setup a secondary observable to tell you what the last item being replayed is:
const last$ = rs.replay(1);
Then you'd just combineLatest and scan ... once you've reached the item emitted by last$ then your ReplaySubject has finishing replaying:
this.sub = Observable.combineLatest(
rs,
last$.take(1)
).scan((acc: { item: Object, isReplay: boolean }, curr: [Object, Object]) => {
return {item: curr[0], isReplay: acc.isReplay && curr[0] !== curr[1]};
}, {isReplay: true}
).subscribe(...);

Get last message of Observable with RxJS

I was wondering whether is it possible to replay or resend the last message of an Observable in RxJS.
Like:
class MyClass {
results: Observable<MyData[]>;
first: MyData;
reactToSmth() {
this.results.subscribe((data: MyData[]) => {
this.first = data[0];
});
}
reactToSmthElse() {
// doesn't exist :-)
this.results.resendLast(data: MyData[]) => {
this.first = data[1];
});
}
}
I know I can simply store data in my class so I can manipulate later, at the same time in a more complex case I would like to know if is possible to do something like that and how.
Thanks.
Well yes, it is possible and even easy. There are several options, but the simplest for what you present is probably, giving obs$ an observable, use obs$.shareReplay(1) to obtain an observable, which when subscribed to will reemit the last emitted value of obs$.

RxJS: How would I "manually" update an Observable?

I think I must be misunderstanding something fundamental, because in my mind this should be the most basic case for an observable, but for the life of my I can't figure out how to do it from the docs.
Basically, I want to be able to do this:
// create a dummy observable, which I would update manually
var eventObservable = rx.Observable.create(function(observer){});
var observer = eventObservable.subscribe(
function(x){
console.log('next: ' + x);
}
...
var my_function = function(){
eventObservable.push('foo');
//'push' adds an event to the datastream, the observer gets it and prints
// next: foo
}
But I have not been able to find a method like push. I'm using this for a click handler, and I know they have Observable.fromEvent for that, but I'm trying to use it with React and I'd rather be able to simply update the datastream in a callback, instead of using a completely different event handling system. So basically I want this:
$( "#target" ).click(function(e) {
eventObservable.push(e.target.text());
});
The closest I got was using observer.onNext('foo'), but that didn't seem to actually work and that's called on the observer, which doesn't seem right. The observer should be the thing reacting to the data stream, not changing it, right?
Do I just not understand the observer/observable relationship?
In RX, Observer and Observable are distinct entities. An observer subscribes to an Observable. An Observable emits items to its observers by calling the observers' methods. If you need to call the observer methods outside the scope of Observable.create() you can use a Subject, which is a proxy that acts as an observer and Observable at the same time.
You can do like this:
var eventStream = new Rx.Subject();
var subscription = eventStream.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
var my_function = function() {
eventStream.next('foo');
}
You can find more information about subjects here:
https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/subject.md
http://reactivex.io/documentation/subject.html
I believe Observable.create() does not take an observer as callback param but an emitter. So if you want to add a new value to your Observable try this instead:
var emitter;
var observable = Rx.Observable.create(e => emitter = e);
var observer = {
next: function(next) {
console.log(next);
},
error: function(error) {
console.log(error);
},
complete: function() {
console.log("done");
}
}
observable.subscribe(observer);
emitter.next('foo');
emitter.next('bar');
emitter.next('baz');
emitter.complete();
//console output
//"foo"
//"bar"
//"baz"
//"done"
Yes Subject makes it easier, providing Observable and Observer in the same object, but it's not exactly the same, as Subject allows you to subscribe multiple observers to the same observable when an observable only send data to the last subscribed observer, so use it consciously.
Here's a JsBin if you want to tinker with it.
var observer = Observable.subscribe(
function(x){
console.log('next: ' +
var my_function = function(){
Observable.push('hello')
One of the way to update an observable.

Resources