I have the following synchronous code that I would like to model as async code in RXJava.
void executeActions(List<Action> action) {
if (action == null || action.size() == 0) return;
for (Action action: actions) {
executeActions(action.handle());
}
}
class Action {
//implementation of handle
// return List<Action> or null.
List<Action> handle() {
}
}
Now in JS I can model this interaction with Promises like so. (Pseudo code below - my JS is weak)
executeActionsAsync(actions) {
var p = Promise.resolve();
action.forEach(function(action) {
p = p.then(function() {
action.handle();
})
}
return p;
}
class Action() {
function handle() {
actions = [];// some array of actions.
executeAsync(actions);
}
}
I would like to model the same in RXJava2. Any help is appreciated.
First of all, Sorry for my bad English.
I edited entire answer because I did not catch what his question is.
I don't know how implement of your Action class's handle function, However this function return value should change to RxJava2's async classes. In this case, Maybe class.
You wants how to implements recursion of async.
Handle List or null.
Use Maybe if you want to handle something or null. in RxJava2
class Action {
Maybe<List<Action>> handle() {}
}
This is what your Action class's handle returns.
void executeActions(Maybe<List<Action>> rxactions) {
// add null check.
// List<Action> handles as stream, but you can use for or iterator or whatever you want.
rxactions.subscribe(actions -> actions.stream().map(action -> executeActions(action.handle())));
}
Important thing is, handle() function returns properly.
Additional
In RxJava2, There are multiple classes to handle async.
Single, Flowable, Observable, Completable. And each classes instance method, subscribe.
Simply say,
1.Single => returns single class.
2.Flowable, Observable => returns multiple classes. (Flowable is more complex than Observable, which added back pressure.)
3.Completable => returns nothing, just succeed or not.
4.Maybe is returns * or null.
5.subscribe is execute this async.
:: Each classes can convert easily.
:: And There are so many ways to solve one problem. so it is just reference.
ex) Single<List<Foo>> <=> Flowable<Foo> // This is not same. but treat as similar.
PS.
I had this experience too. I think you need to learn more about RxJava2 to use properly everywhere.
Promise can devide into Single, Flowable, Observable, Completable. As describe above. This is the KEY to start understanding RxJava2.
Related
I'm trying to extract some common logic, based on RxJava2, into reusable components. Let's imagine I have the following piece of code:
someSingle
.doOnSuccess { // update UI based on side effect }
.subscribeOn(...)
.observeOn(...)
.subscribe(
value -> // update UI based on value
throwable -> // handle error
)
I want to wrap this into a reusable component, exposing a method that returns a Flowable of events. The clients will receive events and update the UI accordingly. My goal is not to have any reference of the view inside the reusable component. I want the method to be something like this:
fun reusableMethod(...) : Flowable<Event> { ... }
Event is a sealed class, enclosing two sub types - SideEffectEvent and ValueEvent.
What is the best way to transform the stream from the first snippet, so I can get both the side effect and the value to be emitted as flowable values?
Currently, I have the following solution, but I'm not very happy with it, because it looks a bit clunky and complex:
private val sideEffectEvents = PublishProcessor.create<SideEffectEvent>()
fun reusableMethod(...) =
Flowable.merge(
someSingle.doOnSuccess { sideEffectEvents.onNext(SideEffectEvent()) },
sideEffectEvents
)
.subscribeOn(...)
.observeOn(...)
I have also considered some alternatives:
Notify the client for SideEffectEvents using a callback that is passed to someReusableMethod() - looks very unnatural and having a callback and a stream to subscribe to is not a good code style
Use a single PublishProcessor. Post side effects to it and use it to subscribe to the original Single. Expose a cleanUp() method in the reusable component so the client can dispose of the stream when it decides to.
I'm looking forward to suggestions and ideas.
First of all it doesn't have to be a Flowable. It can be a simple Observable. But the below solution should work in both cases. read more here Observable vs Flowable
This code is not tested, I have written it to give you a simplified idea about how you can achieve this.
// a sealed class representing current state
sealed class ViewState {
object Loading : ViewState() // using object because we do not need any data in cass of loading
data class Success(val data: List<Model>) : ViewState()
data class Error(val t: Throwable) : ViewState()
}
// an observalbe or flowable returning a single object ViewState
// it will always return ViewState class containing either data or error or loading state
return service.getData()
.map { data -> ViewState.Success(data) } // on successful data fetch
.startWith(ViewState.Loading()) // show loading on start of fetch
.onErrorReturn { exception -> ViewState.Error(exception) } // return error state
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
// somewhere in Activity or in multiple activities subscribe to above observable
subscribe({ viewState ->
when {
viewState.Loading -> showProgressView()
viewState.Error -> showErrorView(viewState.t)
viewState.Success -> showData(viewState.data)
else -> IllegalArgumentException("Invalid Response")
}
})
How about this:
Before:
someSingle
.operation1()
.operation2()
.doOnSuccess { // update UI based on side effect }
.operation3()
.operation4()
.subscribeOn(...)
.observeOn(...)
.subscribe(
value -> // update UI based on value
throwable -> // handle error
)
Reusable:
fun reusableMethod(...): Flowable<Event> =
someSingle
.operation1()
.operation2()
.flatMapPublisher {
Single.concat(
Single.just(getSideEffectEvent(it)),
Single.just(it)
.operation3()
.operation4()
.map { value -> getValueEvent(value) }
)
}
.subscribeOn(...)
.observeOn(...)
You can further simplify this using Flowable#startWith, and avoiding Single#concat()
Is conditional composition of Consumers possible in Java 8? Basically I'm looking to create a custom Lambda interface similar to Consumer but that only works with one type of object. Let's call it, Stateful and it contains multiple statuses (we'll say two for the purpose of this example):
public class Stateful {
private int status1;
private int status2;
}
We have a lot of areas in our code where we do an operation on a Stateful and, if the status has changed, we would do another operation. I was wondering if we could use composition to handle this in a more compact and elegant manner. Right now we would do something like:
SimpleEntry<Integer, Integer> oldStates = new SimpleEntry(stateful.getStatus1(), stateful.getStatus2());
applyLogicOnStateful(stateful); //do some operation that may change state values
if(isStatusChanged(oldStates, stateful) { //compare oldStates integers to status integers
doSomethingElse(stateful);
}
where I think something like this would look better:
statefulConsumer
.accept((stateful)->applyLogicOnStateful(stateful))
.ifStatusChanged((stateful)->doSomethingElse(stateful));
but I don't know if we would be able to track the change in status from before the first consumer to after. Maybe I need to create a lambda that takes two consumers as input?
I'm definitely looking to do this without the assistance of a 3rd party library, although you're welcome to promote one here if it is helpful.
Here is a function that will return a Consumer<Stateful> that will extract the former state, do the change, compare results, and conditionally operate on the changed object.
public static Consumer<Stateful> getStatefulConsumer(
Function<Stateful,SimpleEntry<Integer,Integer>> getStatus, // extract status from Stateful
Consumer<Stateful> applyLogic, // apply business logic
BiPredicate<SimpleEntry<Integer,Integer>,SimpleEntry<Integer,Integer>> compareState, // test statuses for change
Consumer<Stateful> onChange) // doSomethingElse
{
return stateful -> {
SimpleEntry<Integer,Integer> oldStatus = getStatus.apply(stateful);
applyLogic.accept(stateful);
if(!compareState.test(oldStatus, getStatus.apply(stateful))){
onChange.accept(stateful);
}
};
}
You might use it like this:
Consumer<Stateful> ifChanged = getStatefulConsumer(s -> new SimpleEntry<> ( s.status1, s.status2 ),
s -> changeSomething(s), Objects::equals, s->doSomething(s));
You could generify the extracted status so that different stateful types could have different extracted status types, or even use Stateful::clone to copy the status.
The solution I am working with right now is to create a Lambda interface that takes the Stateful instance and two Consumers as input:
public interface StatefulConsumer {
void accept(Stateful stateful, Consumer<Stateful> consumer, Consumer<Stateful> ifStateChangedConsumer);
}
and an implementation:
final StatefulConsumer IfStateChanges = new StatefulConsumer() {
#Override
public void accept(Stateful stateful, Consumer<Stateful> consumer, Consumer<Stateful> ifStateChangedConsumer) {
SimpleEntry<Integer, Integer> oldStates = new SimpleEntry(stateful.getStatus1(), stateful.getStatus2());
consumer.accept(stateful); //do some operation that may change state values
if(isStatusChanged(oldStates, stateful) { //compare oldStates integers to status integers
ifStateChangedConsumer.accept(stateful);
}
}
};
which could be called like this:
IfStateChanges.accept(stateful,
(Stateful s)->applyLogicOnStateful(stateful),
(Stateful s)->doSomethingElse(stateful))
It could also be implemented as a Predicate or a Function that takes a stateful and a consumer as input and returns a boolean for use in an if Statement
Can someone explain why the below test fails?
public class ObservableTest {
#Test
public void badObservableUsedTwiceDoesNotEmitToSecondConsumer() {
// Any simpler observable makes the test pass
Observable<Integer> badObservable = Observable.just(1)
.zipWith(Observable.just(2), (one, two) -> Observable.just(3))
.flatMap(observable -> observable);
ObservableCalculator calc1 = new ObservableCalculator(badObservable);
ObservableCalculator calc2 = new ObservableCalculator(badObservable);
// zipping causes the failure
// Calling calculate().toBlocking().subscribe() on each calc passes
// Observable.from(listOfCalcs).flatMap(calc -> calc.calculate()) passes
Observable.zip(ImmutableList.of(calc1.calculate(), calc2.calculate()), results -> results)
.toBlocking()
.subscribe();
assertThat(calc1.hasCalculated).isTrue();
assertThat(calc2.hasCalculated).isTrue(); // this fails
}
private static class ObservableCalculator {
private final Observable<?> observable;
public boolean hasCalculated = false;
public ObservableCalculator(Observable<?> observable) {
this.observable = observable;
}
public Observable<Void> calculate() {
return observable.concatMap(o -> {
hasCalculated = true;
// returning Observable.just(null) makes the test pass
return Observable.empty();
});
}
}
}
I've tried to simplify the "bad" observable further, but can't find anything I can remove to make it simpler.
My current understanding, though, is that it's an Observable which (regardless of how it's constructed), should emit a single value and then complete. We then make two similar instances of an object based on that Observable, and call a method on those objects which consumes the Observable, makes a note of having done so, and then returns Observable.empty().
Can anyone explain why using this observable causes the test the fail (when using a simpler observable causes the test to pass)?
It's also possible to make the test pass by either serially calling calculate().toBlocking().subscribe() rather than using zip, or making calculate return Observable.just(null) instead. That makes some sense to me (zip won't subscribe to calc2 if calc1 is empty, since it in that case zip could never yield anything), but not complete sense (I don't understand why zip doesn't behave like that for a simpler version of badObservable - the calculate() methods still return empty, regardless of that input).
If you zip an empty source with something, the operator detects it can't produce any value anymore and unsubscribes from all of its sources. There is a mix of zip and merge involved and merge takes unsubscription seriously: it doesn't emit the value 3 at all thus concatMap doesn't call the mapping function for the second source either.
I know that the apply method of Function returns an object synchronously, and the apply of AsyncFunction runs asynchronously and returns a Future.
Can you give me an example of when to prefer what.
One code snippet that I saw looked something like this:
Futures.transform(someFuture, new AsyncFunction<A, B>() {
public B apply(A a) {
if (a != null) {
return Futures.immediateFuture(a.getData())
} else {
return Futures.immediateFailedFuture(checkException(());
}
});
});
Since the value inside AsyncFunction is returned as immediate result, why is AsyncFunction needed here? Or is this just a bad example that I came across?
The code snippet you found is a bad example, since it uses an AsyncFunction for something that is computed synchronously. It's needlessly verbose.
The code would be cleaner using a standard Function:
Futures.transform(someFuture, new Function<A, B>() {
public B apply(A a) {
if (a != null) {
return a.getData();
} else {
throw checkException();
}
});
});
You should use an AsyncFunction when the code that transforms A to B is asynchronous. In your example, it's possible that the code was asynchronous at first, and was later changed to use Futures.immediateFuture() / Futures.immediateFailedFuture() by a programmer who didn't bother replacing the AsyncFunction with a Function. Or maybe he just missed the overloaded method.
Since the value inside AsyncFunction is returned as immediate result,
why is AsyncFunction needed here? Or is this just a bad example that I
came across?
Careful. That piece of code is generating an instance of an anonymous class and passing that instance to the transform method. The transform method will use the AsyncFunction is a separate thread. The Future returned chains to retrieve the results from the AsyncFunction and return the result of that Future. This code still involves asynchronous processing.
Use asynchronous processing when you want to and can continue doing work while something else is being executed.
The code snippet you gave is a bad example, whoever wrote it should have used Function.
Futures.transform() is used to follow up on some asynchronous process. Lets call it "ap1" for "asynchronous process 1". When ap1 is done, the function chained by transform will execute.
Now, lets discuss the 2 types of functions you can chain with Futures.transform().
// AsyncFunction() example:
ListenableFuture<B> future2 = Futures.transform(future1, new AsyncFunction<A, B>() {
public ListenableFuture<B> apply(A a) {
if (a != null) {
ListenableFuture<B> future3 = asyncCallToOtherService(a);
return future3;
}
else {
return Future.immediateFuture(null);
}
});
});
// more complex AsyncFunction() example:
ListenableFuture<B> future2 = Futures.transform(future1, new AsyncFunction<A, B>() {
public ListenableFuture<B> apply(A a) {
if (a != null) {
ListenableFuture<C> future3 = asyncCallToOtherService(a);
return Futures.transform(future3, new Function<C, B>() {
#Override
public B apply(C c) {
B b = new B();
b.setResult(c.getStatus());
return b;
}
});
}
else {
return Future.immediateFuture(null);
}
});
});
// Function() example:
ListenableFuture<B> future2 = Futures.transform(future1, new Function<A, B>() {
public B apply(A a) {
if (a != null) {
B b = new B();
b.setResult(a.getStatus());
return b;
}
else {
return null;
}
});
});
AsyncFunction() should be used when the code inside the apply() spawns another asynchronous process 2 (AP2). This forms a sequence of asynchronous calls that follow each other: AP1->AP2.
Function() should be used when the transformation of the result from AP1 does not require any additional asynchronous processes. Rather all it does is translate the result Object1 into some other Object2 in a synchronous manner.
AsyncFunction() is slightly heavier and results in separate thread, therefore we should use Function() whenever we don't need to spin AP2.
Multiple Functions and AsyncFunctions can be chained together as needed in order to make a complete workflow. The example "more complex AsyncFunction() example" chains A~>C->B transformations, with A~>C being asynchronous.
Using AsyncFunction makes sense here. If you want to throw some checked exception out of the apply() method in Function, it would complain it's not handled. You can not throw it out of the apply() for it's overridden. So If you want to throw some checked exception, AsyncFunction should be a valid solution. For those saying the given example is bad and given Function example, can you please try compile it?
I'm trying to use observable in my code and there is this problem giving me hard time.
public class observeState extends Observable
{
public void setSelectedTransaction(int idx)
{
if (selectedTransaction != idx)
{
this.selectedTransaction = idx;
setChanged();
notifyObservers("setSelectedTransaction");
System.out.println("Observers : "+this.countObservers());
}
}
public void setLog(Log log)
{
if(theLog != log) {
theLog = log;
System.out.println(theLog.getLogTransactions().size() + "setLog");
setChanged();
notifyObservers("setLog");
System.out.println("Observers : "+this.countObservers());
}
}
There are two observers observing this observable class and it does send out notifyObservers when the setSelectedTransaction method is called with the test line "Observers : 2". However the next method setLog does not seem to have observers giving "Observers : 0". I don't think I can only use observable method once.
The mostly likely cause of this issue is that you are not calling the method on the same object. It is a common mistake to assume two objects are the same because they have the same name or some other confusion. I would print out the hashCode of each object or use a debugger to ensure you really are calling the same object.
BTW you can try making the calls in the opposite order, or more than once
to test your theory.
Either the objects that you are using to call the setSelectedTransaction and setLog are different or the observers might be removing themselves as observers in the update method.