How to `Trigger` in Reactive Extension in F#? - rxjs

I did this in F# for FRP that works simply as expected:
let print = fun a -> printf "%A\n" a
let event = new Event<_>()
let stream = event.Publish
stream |> Observable.add (fun event -> event |> print)
event.Trigger 5
Although I don't like much about event.publish system, at least, event.Trigger is somewhat straight forward to understand.
Now, I try to get to used to https://reactivex.io/
I have recognized Rx for a long time since its beta release, and I also know this API is very complicated just to do FRP, like with many "rules" like observable / observer and subjectetc., in my view, this is against KISS principle, so haven't touched.
In fact, a weird thing is for an unknown reason, I can't figure out how to do event.Trigger in Rx.
Surely, I googled a lot, and found a little information for this:
RxJS: How would I "manually" update an Observable?
According to this QA, the code for RxJS is
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');
}
After many trials, I finally discovered that the code below works, with luck
let stream2 = 7 |> Subject.behavior
stream2
|> Observable.map id
|> Observable.subscribe print
|> ignore
stream2.OnNext 99
However, unfortunately, this is only my Guess simply because there's no such a documentation in https://reactivex.io/documentation/subject.html and there is an external documentation http://xgrommx.github.io/rx-book/content/subjects/subject/index.html
The all I know is this code works as intended.
So, my question here is
Is this the only way to "trigger the value" based on the Rx API design?

You seem to undestand Rx basic terms: IObservable and IObserver. These API:s aren't really that complicated. F# makes it even easier since Events implement IObservable out of the box.
It seems that by trigger you mean "make an Observable emit a value" ( OnNext):
If your Observable is created from certain events, triggering such an event will produce a value.
If you want to programatically produce a value using a Subject is fine. As stated in the documentation you pasted, it implements both IObservable and IObserver. E.g. you can call OnNext and Subscribe for the object.
I suggest you consider if and why you really need to programatically produce a value in the Observable. Usually you don't since Observables are created from event sources outside your code. Some cases justify using a Subject such as writing unit tests.

Related

how to rxswift Observable to value?

I'm currently using RIBs and ReactorKit to bind networking data.
The problem here is that the network results come out as Observables, which I have a hard time binding to ReactorKit.
Please let me know if there is a way to strip the Observable or turn it into a value.
Just like when BehaviorRelay is .value, the value comes out...
dependency.loadData.getData().flatMap { $0.detailData.flatMap { $0.result }}
====>> Obervable
now what do i do? TT
Please let me know if there is a way to strip the Observable or turn it into a value.
This is called "leaving" or "breaking" the monad and is a code smell.
In production code, it is rarely advised to 'break the monad', especially moving from an observable sequence to blocking methods. Switching between asynchronous and synchronous paradigms should be done with caution, as this is a common root cause for concurrency problems such as deadlock and scalability issues.
-- Intro to Rx
If you absolutely have to do it, then here is a way:
class MyClass {
private (set) var value: Int = 0
private let disposeBag = DisposeBag()
init(observable: Observable<Int>) {
observable
.subscribe(onNext: { [weak self] new in
self?.value = new
}
.disposed(by: disposeBag)
}
}
With the above, when you query value it will have the last value emitted from the observable. You risk race conditions doing this and that's up to you to deal with.
That's the direct answer to your question but it isn't the whole story. In ReactorKit, the API call should be made in your reactor's mutate() function. That function returns an Observable<Mutation> so instead of breaking the monad, you should be just mapping the API response into a Mutation which is likely a specific enum case that is then passed into your reduce() function.

Observable unsubscribe inside subscribe method

I have tried to unsubscribe within the subscribe method. It seems like it works, I haven't found an example on the internet that you can do it this way.
I know that there are many other possibilities to unsubscribe the method or to limit it with pipes. Please do not suggest any other solution, but answer why you shouldn't do that or is it a possible way ?
example:
let localSubscription = someObservable.subscribe(result => {
this.result = result;
if (localSubscription && someStatement) {
localSubscription.unsubscribe();
}
});
The problem
Sometimes the pattern you used above will work and sometimes it won't. Here are two examples, you can try to run them yourself. One will throw an error and the other will not.
const subscription = of(1,2,3,4,5).pipe(
tap(console.log)
).subscribe(v => {
if(v === 4) subscription.unsubscribe();
});
The output:
1
2
3
4
Error: Cannot access 'subscription' before initialization
Something similar:
const subscription = of(1,2,3,4,5).pipe(
tap(console.log),
delay(0)
).subscribe(v => {
if (v === 4) subscription.unsubscribe();
});
The output:
1
2
3
4
This time you don't get an error, but you also unsubscribed before the 5 was emitted from the source observable of(1,2,3,4,5)
Hidden Constraints
If you're familiar with Schedulers in RxJS, you might immediately be able to spot the extra hidden information that allows one example to work while the other doesn't.
delay (Even a delay of 0 milliseconds) returns an Observable that uses an asynchronous scheduler. This means, in effect, that the current block of code will finish execution before the delayed observable has a chance to emit.
This guarantees that in a single-threaded environment (like the Javascript runtime found in browsers currently) your subscription has been initialized.
The Solutions
1. Keep a fragile codebase
One possible solution is to just ignore common wisdom and continue to use this pattern for unsubscribing. To do so, you and anyone on your team that might use your code for reference or might someday need to maintain your code must take on the extra cognitive load of remembering which observable use the correct scheduler.
Changing how an observable transforms data in one part of your application may cause unexpected errors in every part of the application that relies on this data being supplied by an asynchronous scheduler.
For example: code that runs fine when querying a server may break when synchronously returned a cashed result. What seems like an optimization, now wreaks havoc in your codebase. When this sort of error appears, the source can be rather difficult to track down.
Finally, if ever browsers (or you're running code in Node.js) start to support multi-threaded environments, your code will either have to make do without that enhancement or be re-written.
2. Making "unsubscribe inside subscription callback" a safe pattern
Idiomatic RxJS code tries to be schedular agnostic wherever possible.
Here is how you might use the pattern above without worrying about which scheduler an observable is using. This is effectively scheduler agnostic, though it likely complicates a rather simple task much more than it needs to.
const stream = publish()(of(1,2,3,4,5));
const subscription = stream.pipe(
tap(console.log)
).subscribe(x => {
if(x === 4) subscription.unsubscribe();
});
stream.connect();
This lets you use a "unsubscribe inside a subscription" pattern safely. This will always work regardless of the scheduler and would continue to work if (for example) you put your code in a multi-threaded environment (The delay example above may break, but this will not).
3. RxJS Operators
The best solutions will be those that use operators that handle subscription/unsubscription on your behalf. They require no extra cognitive load in the best circumstances and manage to contain/manage errors relatively well (less spooky action at a distance) in the more exotic circumstances.
Most higher-order operators do this (concat, merge, concatMap, switchMap, mergeMap, ect). Other operators like take, takeUntil, takeWhile, ect let you use a more declarative style to manage subscriptions.
Where possible, these are preferable as they're all less likely to cause strange errors or confusion within a team that is using them.
The examples above re-written:
of(1,2,3,4,5).pipe(
tap(console.log)
first(v => v === 4)
).subscribe();
It's working method, but RxJS mainly recommend use async pipe in Angular. That's the perfect solution. In your example you assign result to the object property and that's not a good practice.
If you use your variable in the template, then just use async pipe. If you don't, just make it observable in that way:
private readonly result$ = someObservable.pipe(/...get exactly what you need here.../)
And then you can use your result$ in cases when you need it: in other observable or template.
Also you can use pipe(take(1)) or pipe(first()) for unsubscribing. There are also some other pipe methods allowing you unsubscribe without additional code.
There are various ways of unsubscribing data:
Method 1: Unsubscribe after subscription; (Not preferred)
let localSubscription = someObservable.subscribe(result => {
this.result = result;
}).unsubscribe();
---------------------
Method 2: If you want only first one or 2 values, use take operator or first operator
a) let localSubscription =
someObservable.pipe(take(1)).subscribe(result => {
this.result = result;
});
b) let localSubscription =
someObservable.pipe(first()).subscribe(result => {
this.result = result;
});
---------------------
Method 3: Use Subscription and unsubscribe in your ngOnDestroy();
let localSubscription =
someObservable.subscribe(result => {
this.result = result;
});
ngOnDestroy() { this.localSubscription.unsubscribe() }
----------------------
Method 4: Use Subject and takeUntil Operator and destroy in ngOnDestroy
let destroySubject: Subject<any> = new Subject();
let localSubscription =
someObservable.pipe(takeUntil(this.destroySubject)).subscribe(result => {
this.result = result;
});
ngOnDestroy() {
this.destroySubject.next();
this.destroySubject.complete();
}
I would personally prefer method 4, because you can use the same destroy subject for multiple subscriptions if you have in a single page.

RxSwift - How to create two streams from one upstream

Background
I'm trying to observe one Int stream (actually I'm not, but to make the argument easier) and do something with it while combining that stream to multiple other streams, say a String stream and a Double stream like the following:
// RxSwift
let intStream = BehaviorSubject<Int>(value: 0) // subscribe to this later on
let sharedStream = intStream.share()
let mappedStream = sharedStream.map { ... }.share()
let combinedStream1 = Observable.combineLatest(sharedStream, stringStream).map { ... }
let combinedStream2 = Observable.combineLatest(sharedStream, doubleStream).map { ... }
The above code is just to demonstrate what I'm trying to do. The code above is part of view model code (the VM part of MVVM), and only the first map (for mappedStream) runs, while the others are not called.
Question
What is wrong with the above approach, and how do I achieve what I'm trying to do?
Also, is there a better way to achieve the same effect?
Updates
I confirmed that setting the replay count to 1 makes things work. But why?
The code above all goes in the initialization phase of the view model, and the subscription happens afterwards.
Okay, I have an answer but it's a bit complex... One problem is that you are using a Subject in the view model, but I'll ignore that for now. The real problem comes from the fact that you are using hot observables inappropriately (share() make a stream hot) and so events are getting dropped.
It might help if you put a bunch of .debug()s on this code so you can follow along. But here's the essence...
When you subscribe to mappedStream, it subscribes to the share which in turn subscribes to the sharedStream, which subscribes to the intStream. The intStream then emits the 0, and that 0 goes down the chain and shows up in the observer.
Then you subscribe to the combinedStream1, which subscribes to the sharedStream's share(). Since this share has already been subscribed to, the subscriptions stop there, and since the share has already output it's next event, the combinedStream1 doesn't get the .next(0) event.
Same for the combinedStream2.
Get rid of all the share()s and everything will work:
let intStream = BehaviorSubject<Int>(value: 0) // subscribe to this later on
let mappedStream = intStream.map { $0 }
let combinedStream1 = Observable.combineLatest(intStream, stringStream).map { $0 }
let combinedStream2 = Observable.combineLatest(intStream, doubleStream).map { $0 }
This way, each subscriber of intStream gets the 0 value.
The only time you want to share is if you need to share side effects. There aren’t any side effects in this code, so there’s no need to share.

what is difference between do(onNext:) and subscribe(onNext:)?

I'm new in RxSwift, I don't understand what is difference between do(onNext:) and subscribe(onNext:).
I google it but did't found good resources to explain the difference.
At the beginning of a cold Observable chain there is a function that generates events, for e.g. the function that initiates a network request.
That generator function will not be called unless the Observable is subscribed to (and by default, it will be called each time the observable is subscribed to.) So if you add a do(onNext:) to your observable chain, the function will not be called and the action that generates events will not be initiated. You have to add a subscribe(onNext:) for that to happen.
(The actual internals are a bit more complex than the above description, but close enough for this explanation.)
The do operator allows you to insert side effects; that is, handlers to do things that will not change the emitted event in any way. do will just pass the event through to the next operator in the chain.
The method for using the do operator is here.
And you can provide handlers for any or all of these events.
Let's say We have an observable that never emits anything. Even though it emits nothing, it is still an observable and we can subscribe to it. do operator allows us to do something when a subscription was made to it.
So below example will print "Subscribed" when a subscription was made to that observable.
Feel free to include any of the other handlers if you’d like; they work just like subscribe’s handlers do
let observable = Observable<Any>.never()
let disposeBag = DisposeBag()
observable
.do(onSubscribe: {
print("Subscribed")
})
.subscribe(
onNext: { element in
print(element)
},
onCompleted: {
print("Completed")
},
onDisposed: {
print("Disposed")
}
)
.disposed(by: disposeBag)

RxJS - FlatMap observer called multiple times

I'm trying to understand how flatMap works. I understand that it is a way for handling Observable< Observable < T>>.
Anyhow, I was testing the behaviour of it and got stuck with this:
let plusDom = document.querySelector('#plus');
let minusDom = document.querySelector('#minus');
let minusSource = Rx
.Observable
.fromEvent(minusDom, 'click');
let plusSource = Rx.Observable
.fromEvent(plusDom, 'click');
plusSource
.flatMap(function(c){
console.log('Flatmap called');
return minusSource;
})
.subscribe(function(e){
console.log('done');
})
Here it is the jsbin: https://jsbin.com/sefoyowuqe/edit?html,js,console,output
I don't understand this behaviour:
3 clicks on plusDom prints:
Flatmap called
Flatmap called
Flatmap called
1 click on minusDom prints:
done
done
done
Why when clicking the minusDom it replays the events as many times as we have click the plusDom?
flatMap basically places the returned stream flat into the original stream. What you are probably looking for is switchMap, which will switch to that returned stream and switch to a new stream when the original source emits data by discarding the old one.
When in doubt, switchMap is usually the safest alternative to use.
See the marble-diagrams for comparison:
Flatmap doesn't remove previously "flattened" streams and :
Switchmap removes previously "switched" streams.

Resources