In Reactive Java, we're told that the .subscribe() call returns "a Subscription reference". But Subscription is an interface, not a class. So what kind of object are we handed that implements this interface? Do we have any control over this?
There is the class Subscriptions that can create and return several different kinds of Subscription, but what does one do with them? If I write
Subscription mSub = Subscriptions.create(<some Action0>);
mSub = someObservable.subscribe();
won't my just-created Subscription simply be overwritten by whatever the .subscribe() call returns? How do you use a Subscription you create?
(On a somewhat related note, what is the point of Subscriptions.unsubscribed(), which "returns a Subscription to which unsubscribe does nothing, as it is already unsubscribed. Huh?)
Short answer: You shouldn't care.
Longer answer: a subscription gives you two methods:
unsubscribe(), which causes the subscription to terminate.
isUnsubscribed(), which checks whether that has already happened.
You can use these methods to a) check whether an Observable chain terminated and b) to cause it to terminate prematurely, for example if the user switched to a different Activity.
That's it. You aren't exposed to the internals on purpose. Also, do you notice that there's no resubscribe method? That's because if you want to restart the operation, you need to resubscribe to the Observable, giving you a new Subscription.
As you know Subscriptions are used to keep references to ongoing Observables, mainly for resources' management. For example in Android applications, when you change an Activity (screen) you flush old Activity Observables. In this scenario, Subscription instances are given by .subscribe() (as you mentioned) and stored. So, for which reason would one create a Subscription directly, especially Subscriptions.unsubscribed()? I encountered two cases:
Default implementation; avoid declaration like Subscription mSub; that would be filled latter and could create an NPE. It's especially true if you use Kotlin that require property initialization.
Testing
On a somewhat related note, what is the point of Subscriptions.unsubscribed(), which "returns a Subscription to which unsubscribe does nothing, as it is already unsubscribed. Huh?
In 1.x, Subscriptions.unsubscribed() is used to return a Subscription instance the operation was completed (or never run in the first place) when the control is returned to your code from RxJava. Since being unsubscribed is stateless and a constant state, the returned Subscription is a singleton because just by looking at the interface Subscription there is no (reasonable) way to distinguish one completed/unsubscribed Subscription from another.
In 2.x, there is a public and internal version of its equivalent interface, Disposable. The internal version is employed mostly to swap out a live Disposable with a terminated one, avoiding NullPointerException and null checks in general and to help the GC somewhat.
what does one do with them?
Usually you don't need to worry about Subscriptions.create(); it is provided for the case you have a resource you'd like to attach to the lifecycle of your end-subscriber:
FileReader file = new FileReader ("file.txt");
readLines(file)
.map(line -> line.length())
.reduce(0, (a, b) -> a + b)
.subscribe(new Subscriber<Integer>() {
{
add(Subscriptions.create(() -> {
Closeables.closeSilently(file); // utility from Guava
});
}
#Override public void onNext(Integer) {
// process
}
// onError(), onCompleted()
});
This example, demonstrating one way of usage, can be expressed via using instead nonetheless:
Observable.using(
() -> new FileReader("file.txt"), // + try { } catch { }
file -> readLines(file).map(...).reduce(...),
file -> Closeables.closeSilently(file)
)
.subscribe(...)
Related
I still do not understand when to apply this method. In fact, it is similar to Mono.just, but I heard that callback is used for heavy operations if it needs to be performed separately from other flows. Now I use it like this, but is it correct.
Here is an example of use, I wrap sending a firebase notification in a callback since the operation is long
#Override
public Mono<NotificationDto> sendMessageAllDevice(NotificationDto notification) {
return Mono.fromCallable(() -> fcmProvider.sendPublicMessage(notification))
.thenReturn(notification);
}
maybe I still had to wrap up here in Mono.just ?
It depends which thread you want fcmProvider.sendPublicMessage(...) to be run on.
Either the one currently executing sendMessageAllDevice(...):
T result = fcmProvider.sendPublicMessage(notification);
return Mono.just(result);
Or the one(s) the underlying mono relies on:
Callable<T> callable = () -> fcmProvider.sendPublicMessage(notification);
return Mono.fromCallable(callable);
I would guess you need the latter approach.
If you use Mono.just(computeX()), computeX() is called immediately. No want you want(I guess).
If you use Mono.fromCallable(() -> computeX()), the computation is still not performed. I mean computeX() is only called when you subscribe to it. Maybe using .map, .flatMap, etc.
Important: if computeX() return Mono you doe not need to use Mono.fromCallable. It's only for blocking code
As you explained in the description, Mono.fromCallable is used when you want to compute a result with an async execution (mostly some heavy operation).
Since, you have already generated the Mono with Mono.fromCallable you do not have to wrap it again with Mono.just.
So, I'm trying to work with Webflux and I've got a scenario "check if an object exists; if so, do stuff, else - indicate error".
That can be written in reactor as:
public Mono<Void> handleObjectWithSomeId(Mono<IdType> id){
return id.
flatMap(repository::exists). //repository.exists returns Mono<Boolean>
flatMap(e -> e ? e : Mono.error(new DoesntExistException())).
then(
//can be replaced with just(someBusinessLogic())
Mono.fromCallable(this::someBusinessLogic)
);
}
or as:
public Mono<Void> handleObjectWithSomeId(Mono<IdType> id){
return id.
flatMap(repository::exists). //repository.exists returns Mono<Boolean>
flatMap(e -> e ? e : Mono.error(new DoesntExistException())).
map(e -> this.someBusinessLogic()));
}
Let's assume that return type of someBusinessLogic cannot be changed and it has to be simple void, not Mono<Void>.
In both cases if the object won't exist, appropriate Mono.error(...) will be produced.
While I understand that then and flatMap have different semantics, effectively I get the same result. Even though in second case I'm using flatMap against its meaning, I get to skip flatMap and fromCallable in favor of simple map with ignored argument (that seems more readable). My point is, both apporaches have advantages and disadvantages when it comes to readability and code quality.
So, here's a summary:
Using then
pros
is semantically correct
cons
in many cases (like above) requires wrapping in ad-hoc Mono/Flux
Using flatMap
pros
simplifies continued "happy scenario" code
cons
is semantically incorrect
What are other pros/cons of both approaches? What should I take under consideration when choosing an operator?
I've found this reactor issue that states that there is not real difference in speed.
TL, DR: If you care about the result of the previous computation, you can use map(), flatMap() or other map variant. Otherwise, if you just want the previous stream finished, use then().
You can see a detailed log of execution for yourself, by placing an .log() call in both methods:
public Mono<Void> handleObjectWithSomeId(Mono<IdType> id) {
return id.log()
.flatMap(...)
...;
}
Like all other operations in Project Reactor, the semantics for then() and flatMap() are already defined. The context mostly defines how these operators should work together to solve your problem.
Let's consider the context you provided in the question. What flatMap() does is, whenever it gets an event, it executes the mapping function asynchronously.
Since we have a Mono<> after the last flatMap() in the question, it will provide the result of previous single computation, which we ignore. Note that if we had a Flux<> instead, the computation would be done for every element.
On the other hand, then() doesn't care about the preceding sequence of events. It just cares about the completion event:
That's why, in your example it doesn't matter very much which one you use. However, in other contexts you might choose accordingly.
You might also find the Which operator do I need? section Project Reactor Reference helpful.
Suppose that we need to transform a hot Observable in a way that we need to know all of its previously emitted items to be able to determine what to emit next. The solution which I find the most convenient is to pass an instance of a Func1 subclass, which has a global state (e.g. a map or list of previously emitted items) to flatMap. In each call, the Func1 instance would update its state and based on that, decide what to return.
However, I am worried about the "niceness" of this solution. As far as I know, RxJava does not go well with global and mutable state, which this solution seems to be in contrast with. On the other hand, I am sure that my Observable fulfills the Observable contract, so it seems to be at least a working solution, and event if it could be called concurrently, a synchronization would solve the problem.
Other possible solutions could be:
Creating an Operator. Mutable state in Operators is allowed, I guess. Anyways, I try to avoid custom operators, as they are more tricky.
Propagating the history of the Observable through scan (in a List or Map). I would either use the same object (List or Map) for every emitted item, which introduces a mutable object into the stream, or copy the entire object every time, which would waste a lot of performance.
Subscribe to the original Observable, modify some global state from the subscriber, and emit items on a Subject (the transformed Observable) using this global state. I thought about this because it seems to exit the scope of RxJava when it deals with the global state (and synchronization).
So the question is: Should I use the Func1 implementation with mutable state in flatMap for transforming items based on the history of previously emitted items (which works, btw), and if not, what alternatives should I use? In general, I am confused about the recommended way to handle a complex mutable state needed for the transformation of Observables.
I hope I have expressed my problem clearly. Otherwise, let me know and I will try to describe it with the help of some specific problems and code.
Flows with functions containing mutable state are generally not recommended as the mutable state could be potentially shared across multiple Subscribers to a particular Observable chain. Often though, most developers assemble Observables when needed and rarely ever reuse the same Observable. For example, a button click handler will create an Observable that, through composition, forks off two other Observables to get data from two different places asynchronously, and then subscribe to this thread-local Observable instance. A new button click will repeat the process with a fresh and independent Observable.
Here lies the solution to your stateful-function problem: make the existence of the stateful bits depend on the individual Subscribers subscribing: defer()
Observable<Integer> o = Observable.defer(() -> {
return Observable.range(1, 10)
.map(new Func1<Integer, Integer>() {
int sum;
#Override
public Integer call(Integer v) {
sum += v;
return sum;
}
});
});
o.subscribe(System.out::println);
o.subscribe(System.out::println);
Since the Func1 inner class will be created for each of the subscribe call, its sum field will be local to each individual consumer. Note also that sum is returned and auto-boxed into an immutable Integer which then can be freely read after in some other thread (think observeOn) as it is then completely detached of the sum field then on.
Mutable state and shared, mutable state often are required for useful work. The issue is how well we isolate the mutability from outside parties.
Creating an operator hides the mutability within the operator instance. The downside is that the state is private to the observable chain.
scan(), reduce() and fold() (if it existed) would be good candidates, but they have very limited implementations, export their state in non-obvious ways and are also limited to the observable chain they are attached to.
Subject or Relay objects provide useful cut-out points.
Going back to basics, using a privately accessible data structure in thread-safe ways is not a bad thing. If you are only concerned about the one observer chain, then either of options 1 or 3 will do the job readily.
I'm using a Builder pattern to build upon a model object which combines data from different network calls and I'm having a hard time understanding the best way to take the model object from the first network call and combine the data from the second network call into the original model object.
My actual subscription:
myFirstApiRepository.getFirstModelObjectBuilder()
.flatmap(firstModelObjectBuilder -> mySecondApiRepository.getSomeExtraData(firstModelObjectBuilder))
.observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getMySubscriber());
First network call:
public Observable<FirstModelObject.Builder> getFirstModelObjectBuilder() {
return myFirstApiResource.getSomeData(...)
.flatMap(someData -> Observable.just(new FirstModelObject.Builder()
.setFirstAttribute(someData.getFirstAttribute())
.setSecondAttribute(someData.getSecondAttribute())));
}
Second network call:
public Observable<FirstModelObject> getSomeExtraData(FirstModelObject.Builder builder) {
return mySecondApiResource.getSomeData(...)
.flatMap(aString -> builder.setSomeStringValue(aString)
.build());
}
The problem here is that I have to pass the builder object into the second network call's observable. This makes it very rigid and I'd rather not have my SecondApiRepository rely on and reference a data type which it shouldn't need to reference. It also makes this second method here ".build()" the object, which is not good. So, how can I use the firstModelObject and add data to it from the second network call in a clean way?
If this is just bad design, please let me know. I'm still trying to learn more about RxJava best practices. :)
If your second request relies on first request's result, then check my answer - https://stackoverflow.com/a/41820372/7045114.
If not - just use zip operator:
Combines the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function.
Observable.zip(firstRequest, secondRequest, (firstResult, secondResult) -> {
//process results
})
I started to play around with RxJava and ReactFX, and I became pretty fascinated with it. But as I'm experimenting I have dozens of questions and I'm constantly researching for answers.
One thing I'm observing (no pun intended) is of course lazy execution. With my exploratory code below, I noticed nothing gets executed until the merge.subscribe(pet -> System.out.println(pet)) is called. But what fascinated me is when I subscribed a second subscriber merge.subscribe(pet -> System.out.println("Feed " + pet)), it fired the "iteration" again.
What I'm trying to understand is the behavior of the iteration. It does not seem to behave like a Java 8 stream that can only be used once. Is it literally going through each String one at a time and posting it as the value for that moment? And do any new subscribers following any previously fired subscribers receive those items as if they were new?
public class RxTest {
public static void main(String[] args) {
Observable<String> dogs = Observable.from(ImmutableList.of("Dasher", "Rex"))
.filter(dog -> dog.matches("D.*"));
Observable<String> cats = Observable.from(ImmutableList.of("Tabby", "Grumpy Cat", "Meowmers", "Peanut"));
Observable<String> ferrets = Observable.from(CompletableFuture.supplyAsync(() -> "Harvey"));
Observable<String> merge = dogs.mergeWith(cats).mergeWith(ferrets);
merge.subscribe(pet -> System.out.println(pet));
merge.subscribe(pet -> System.out.println("Feed " + pet));
}
}
Observable<T> represents a monad, a chained operation, not the execution of the operation itself. It is descriptive language, rather than the imperative you're used to. To execute an operation, you .subscribe() to it. Every time you subscribe a new execution stream is created from scratch. Do not confuse streams with threads, as subscription are executed synchronously unless you specify a thread change with .subscribeOn() or .observeOn(). You chain new elements to any existing operation/monad/Observable to add new behaviour, like changing threads, filtering, accumulation, transformation, etc. In case your observable is an expensive operation you don't want to repeat on every subscription, you can prevent recreation by using .cache().
To make any asynchronous/synchronous Observable<T> operation into a synchronous inlined one, use .toBlocking() to change its type to BlockingObservable<T>. Instead of .subscribe() it contains new methods to execute operations on each result with .forEach(), or coerce with .first()
Observables are a good tool because they're mostly* deterministic (same inputs always yield same outputs unless you're doing something wrong), reusable (you can send them around as part of a command/policy pattern) and for the most part ignore concurrence because they should not rely on shared state (a.k.a. doing something wrong). BlockingObservables are good if you're trying to bring an observable-based library into imperative language, or just executing an operation on an Observable that you have 100% confidence it's well managed.
Architecting your application around these principles is a change of paradigm that I can't really cover on this answer.
*There are breaches like Subject and Observable.create() that are needed to integrate with imperative frameworks.