Using RxJava2, I am trying to accomplish some timed events.
I have a few async events that might complete in less than one second, and I want to be displaying a message to the user for at least one second, before continuing on with the result of the async operation (and maybe use that result later).
The async operation may also timeout, and I would want to display a message and not continue.
I can accomplish this by using an zip() with the timer as the first parameter and the async operation as the second operator, but what do I do with the next 'layer'?
This is the code I have so far, which actually does work, but I feel very dirty creating nested subscriptions (using just() in place of the async operation, and ignoring subscription threads)
mStrings is just a BehaviorSubject<String>.
mStrings.onNext("Waiting 1 second. The result will fire at the Single.timer onComplete");
Single.zip(Single.timer(1, TimeUnit.SECONDS), Single.just("First"), (t1, t2) -> t2)
.timeout(15, TimeUnit.SECONDS, observer -> {
mStrings.onNext("First timeout fired");
})
.subscribe(s1 -> {
mStrings.onNext("First timer fired and returned " + s1);
Single.zip(Single.timer(1, TimeUnit.SECONDS), Single.just("Second"), (t1, t2) -> t2)
.timeout(15, TimeUnit.SECONDS, observer -> {
mStrings.onNext("Second timeout fired");
})
.subscribe(s2 -> {
mStrings.onNext("Second timer fired and returned " + s2 + ". Previous was " + s1);
Single.zip(Single.timer(1, TimeUnit.SECONDS), Single.just("Third"), (t1, t2) -> t2)
.timeout(15, TimeUnit.SECONDS, observer -> {
mStrings.onNext("Third timeout fired");
})
.subscribe(s3 -> {
mStrings.onNext("Third timer fired and returned " + s3 + ". Previous was " + s1 + " and " + s2 );
});
});
});
The result of which is:
17:53:53.219 Waiting 1 second. The result will fire at the Single.timer onComplete
17:53:54.220 First timer fired and returned First
17:53:55.224 Second timer fired and returned Second. Previous was First
17:53:56.224 Third timer fired and returned Third. Previous was First and Second
Am I missing an operator that would make sense in this type of flow? Or some elementary methodology? I know I might be able to work out an alternative solution using multiple subjects, but it seems excessive.
I think I fixed my own issue. flatMap can be used to condense all the subscriptions into one, and each individual timer and timeout will still operate as expected.
mStrings.onNext("Waiting 1 second. The result will fire at the Single.timer onComplete");
Single.zip(Single.timer(1, TimeUnit.SECONDS), Single.just("First"), (t1, t2) -> t2)
.timeout(2, TimeUnit.SECONDS, observer -> {
mStrings.onNext("First timeout fired");
})
.flatMap(s1 -> {
mStrings.onNext("First timer fired and returned " + s1);
return Single.zip(Single.timer(1, TimeUnit.SECONDS), Single.just("Second"), (t1, t2) -> t2)
.timeout(15, TimeUnit.SECONDS, observer -> {
mStrings.onNext("Second timeout fired");
})
.flatMap(s2 -> {
mStrings.onNext("Second timer fired and returned " + s2 + ". Previous was " + s1);
return Single.zip(Single.timer(1, TimeUnit.SECONDS), Single.just("Third"), (t1, t2) -> t2)
.timeout(15, TimeUnit.SECONDS, observer -> {
mStrings.onNext("Third timeout fired");
})
.flatMap(s3 -> {
mStrings.onNext("Third timer fired and returned " + s3 + ". Previous was " + s1 + " and " + s2 );
return Single.just("Fourth");
});
});
})
.subscribe(s -> {
// ignored
});
}));
The complex nesting can still be simplified by composing the Observables a bit more logically.
Related
My goal is to understand how CompletableFuture works.
My expected result: If I do CompletableFuture.runAsync().thenRun().thenRunAsync(). The thread will be executed in sequence runAsync() -> thenRun() -> thenRunAsync().
My actual result: The sequence is race condition. Sometimes:
runAsync -> thenRunAsync+e -> ...
runAsync -> thenRun -> ...
Reference from SO
public class RunExample5 {
public static void main(String[] args) {
ExecutorService e = Executors.newSingleThreadExecutor(r -> new Thread(r, "sole thread"));
CompletableFuture<?> f = CompletableFuture.runAsync(() -> {
System.out.println("runAsync:\t" + Thread.currentThread());
LockSupport.parkNanos((int) 1e9);
}, e);
f.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()));
f.thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()));
f.thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()),
e);
LockSupport.parkNanos((int) 2e9);
e.shutdown();
}
}
You need
f.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()))
.thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()))
.thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()), e);
The interface to CompletableFuture doesn't work quite the way you're imagining. f itself doesn't keep track of every call to thenRun or thenRunAsync and run them in order; instead, it treats everything from thenRun or thenRunAsync as simultaneously runnable as soon as the main work completes. If you want to chain more complicated sequences of work, you need to use the return value of thenRun or thenRunAsync -- a CompletionStage object -- and call thenRunAsync on that.
I am trying to learn the basics of RxJava2 library and right now I am stuck at the following moment:
I have generated myFlowable via Flowable.generate(...) and now I need to wait while all the tasks will finish its execution, before I can proceed further.
This is the code to showcase the problem:
myFlowable.parallel()
.runOn(Schedulers.computation())
.map(val -> myCollection.add(val))
.sequential()
.subscribe(val -> {
System.out.println("Thread from subscribe: " + Thread.currentThread().getName());
System.out.println("Value from subscribe: " + val.toString());
});
System.out.println("Before sleep - Number of objects: " + myCollection.size());
try {
Thread.sleep(1000);
System.out.println("After sleep - Number of objects: " + myCollection.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
I run through all my tasks and add the results to collection. And if I check the collection size right after myFlowable block then it will be different, if I check it after small Thread.sleep(). Is there any way to check that all the tasks finished its execution and we can proceed further? Any help or guidance will be greatly appreciated.
As RxJava is asynchronous the java code below observable will run while the observable will run in a different thread thets why if you want to be notified if Flowable has finished emitting data you should do that in RxJava stream. for that you have an operator .doOnComplete
here you have an example how to detect when stream is finished
Flowable.range(0, 100).parallel()
.runOn(Schedulers.computation())
.map(integer -> {
return integer;
})
.sequential()
.doOnComplete(() -> {
System.out.println("finished");
})
.subscribe(integer -> System.out.println(integer));
You could use an AtomicBoolean, initialize it to false and set it to true using doFinally().
doFinally() is called after the Observable signals onError or onCompleted or it gets disposed by the downstream.
Then sleep the main thread until completed value is true.
Using your example:
AtomicBoolean completed = new AtomicBoolean(false);
myFlowable.parallel()
.runOn(Schedulers.computation())
.map(val -> myCollection.add(val))
.sequential()
.doFinally(() -> completed.set(true))
.subscribe(val -> {
...
});
...
try {
while(!completed.get()){
Thread.sleep(1000);
...
}
...
} catch (InterruptedException e) {
e.printStackTrace();
}
Use Flowable::blockingSubscribe() - Runs the current Flowable to a terminal event, ignoring any values and rethrowing any exception.
http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Flowable.html#blockingSubscribe--
Say I have a method which takes a parameter and returns a Mono<Integer> that asynchronously completes. For example:
Random random = new Random();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(16);
Mono<Integer> fetch(String a) {
return Mono.create(em -> {
scheduledExecutorService.schedule(() -> em.next(a + " result"),
10 + random.nextInt(50), TimeUnit.MILLISECONDS);
});
}
Say I have a Flux<String> that I can feed into the fetch method above and that can have a lot of elements.
Is there a way I can ensure that the method gets called in parallel, but limit the number of concurrent calls to a predefined number?
E.g. 4 in the above example, while I have 16 available threads - so I always keep 12 spare from this perspective.
Assuming by "feed into", you mean you are using flux.flatMap(this::fetch),
then you can set the flatMap concurrency by calling flux.flatMap(this::fetch, 4) instead.
Also, your code has two compile errors:
the return type of fetch Mono<Integer> does not match the type of item you are giving to the sink (a + " result"). I assume you meant Mono<String>
MonoSink does not have a .next method. I assume you meant .success
Given all of that, here's an example:
private Flux<String> fetchAll() {
return Flux.range(0, 50)
.map(i -> Integer.toString(i))
.flatMap(this::fetch, 4);
}
private Mono<String> fetch(String a) {
return Mono.create(em ->
scheduledExecutorService.schedule(() -> em.success(a + " result"),
10 + random.nextInt(50), TimeUnit.MILLISECONDS)
);
}
I'm new to RX java and I've been experimenting with observeOn and subscribeOn methods. I've read that the difference between them is that they affect the whole chain(subscribeOn) or only the part of the chain after setting a scheduler(observeOn). So why the code below executes fine (prints the current thread):
Observable obs = Observable.from(Arrays.asList("element", "2nd element"));
obs.observeOn(Schedulers.newThread())
.map(x -> x.toString().toUpperCase())
.subscribe(x -> System.out.println("NT:" + Thread.currentThread().getName() + x));
while this code doesn't print anything:
Observable obs = Observable.from(Arrays.asList("element", "2nd element"));
obs.subscribeOn(Schedulers.newThread())
.map(x -> x.toString().toUpperCase())
.subscribe(x -> System.out.println("NT:" + Thread.currentThread().getName() + x));
Are you sure that this code doesn't print anything?
I tried this code:
Observable obs = Observable.from(Arrays.asList("element", "2nd element"));
obs.subscribeOn(Schedulers.newThread())
.map(x -> x.toString().toUpperCase())
.subscribe(x -> System.out.println("NT:" + Thread.currentThread().getName() + x));
Thread.sleep(5000);
Output:
NT:RxNewThreadScheduler-1ELEMENT
NT:RxNewThreadScheduler-12ND ELEMENT
Maybe you forgot to sleep or do some another work to make application wait for completion of new RxJava threads?
Basically, an observable (more specifically, a cold observable) is a delayed calculation. So, .subscribeOn() defined on which scheduler/thread this calculation will be done, and .observeOn() defines on which scheduler/thread you will receive its result. For instance, network request should run on Schedulers.io which you specify by adding .subscribeOn(Schedulers.io) and you want to display results on main thread by specifying .observeOn(AndroidShedulers.main)
As per my understanding,
The SubscribeOn operator specifies the Thread in which the
Observable source should start emission.
If you have multiple SubscribeOn on a chain, the first one takes
effect for the entire flow.
ObserveOn can be used to flip the Thread to the downstream at any
point, Whenever an ObserveOn present in a chain, it changes the
thread for the downstream
But I tried a sample with Subject and I can see theres no effect for SubscribeOn in the entire chain.
Here's my sample
Subject<String> mSubject = PublishSubject.create();
I consume it like
mSubject
.map(s -> "String :" + s)
.doOnNext(s -> Log.d(TAG, "Started at Thread :" + Thread.currentThread().getName()))
.flatMap(s -> Observable.just(1))
.map(Object::toString)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
Log.d(TAG, "subscribed: " + s + " at " + Thread.currentThread().getName());
});
and trigger from some other place
mSubject.onNext("hello");
Here you can see, I have given .subscribeOn(Schedulers.io()) , So I expect
mSubject
.map(s -> "String :" + s)
.doOnNext(s -> Log.d(TAG, "Started at Thread :" + Thread.currentThread().getName()))
.flatMap(s -> Observable.just(1))
.map(Object::toString)
till these to execute in a Scheduler Thread. Then I flip the thread Using observeOn to the main thread. But here the log for this code
D/MainActivity: Started at Thread :main
D/MainActivity: subscribed: 1 at main
Why didn't it start on Scheduler thread?
The same I tried using normal Observable without using a subject.
Observable.just("Hello")
.map(s -> "String :" + s)
.doOnNext(s -> Log.d(TAG, "Started at Thread :" + Thread.currentThread().getName()))
.flatMap(s -> Observable.just(1))
.map(Object::toString)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
Log.d(TAG, "subscribed: " + s + " at " + Thread.currentThread().getName());
});
and here's the log
D/MainActivity: Started at Thread :RxCachedThreadScheduler-2
D/MainActivity: subscribed: 1 at main
I can see it is working as expected!
So what happened with the subjects
From the post Using subjects (which is linked by the official RxJava Subject documentation), it states:
By default, subjects do not perform any synchronization across threads. They do not take a scheduler but rather assume that all serialization and grammatical correctness are handled by the caller of the subject.
So, to my best understanding this means that the thread used is the one of the code calling the onNext(), and later is sent to the observed thread.