RxJava unsubscribe on Network close - java

i am currently trying to unsubscribe an observable, if the network request is closed, to not further stream the data.
My Observable is created with:
Observable.interval(1, TimeUnit.SECONDS)
.map { LocalDateTime.now() }.share()
So there are multiple subscribers. But I don't know how to unsubscribe if the network is closed.
I am currently stream the data with server-sent event with vert.x to the client like this:
flow.subscribe({
response.write("the current time is $it")
}, ::println, {
response.end()
})
If I cancel the request from the client, the observable will continue to "stream" the data.
thanks for the help

You can unsubscribe subscriber by calling dispose()
Disposable disposable = flow.subscribe({
response.write("the current time is $it")
}, ::println, {
response.end()
})
disposable.dispose();
UPDATED: custom observable
val observable: Observable<LocalDateTime> = Observable.create { emitter ->
val timer = Timer()
timer.schedule(timerTask {
if (emitter.isDisposed) {//<-- cancel emmitting items if disposed
timer.cancel()
} else {
emitter.onNext(LocalDateTime.now())
}
}, 0, 1000)
}
disposable = observable.share().subscribe { t ->
System.out.println(" Hello World! $t");
disposable?.dispose()//<-- here calling dispose() causes observable to stop emitting items
}

You can use takeUntil operator with response closeHandler:
router.get("/test").handler(ctx -> {
ctx.response().setChunked(true);
Observable.interval(1, TimeUnit.SECONDS)
.share()
.doFinally(() -> System.out.println("Disposed"))
.takeUntil(Observable.create(e -> ctx.response().closeHandler(event -> e.onComplete())))
.subscribe(l -> ctx.response().write(l.toString()));
});

Related

How to continue polling with observable interval if error occurs

I have a simple network polling function with observable intervals
private fun pollFromApi(): Observable<MyState> {
return Observable.interval(3L, TimeUnit.SECONDS, schedulerProvider.io())
.startWith(0L)
.flatMap {
api.getState().toObservable()
}
.map {
// map response to MyState
}
.onErrorReturn {
return#onErrorReturn MyState.Polling // if error occurred emit Polling State
}
.takeUntil {
// stop polling if certain State is reached
}
}
The problem I have is that if in the middle of polling one of the network API calls fails, the polling stops. Ideally what I want is to keep retrying until takeUntil stops the polling and if an error occurs, just ignore it and do not emit anything to observers.
I tried adding onErrorReturn but that just catches the error and stops the polling.
You can use Observable#onErrorResumeNext operator chained to your remote (possibly failing) API call, emitting an item that does not meet your #takeUntil clause to avoid stopping processing:
private fun pollFromApi(): Observable<MyState> {
return Observable.interval(3L, TimeUnit.SECONDS, schedulerProvider.io())
.startWith(0L)
.flatMap {
api.getState().toObservable().onErrorResumeNext(ignored -> null) // or some other SENTINEL value
}
.map {
// map response to MyState
}
.takeUntil {
// stop polling if certain State is reached
}
}
As I mentioned in the comments, you'll have to do the mapping and error handling on the api call itself inside the flatMap:
private fun pollFromApi(): Observable<MyState> {
return Observable.interval(3L, TimeUnit.SECONDS, schedulerProvider.io())
.startWith(0L)
.flatMap {
api.getState().toObservable()
.map {
// map response to MyState
}
.onErrorReturn {
return#onErrorReturn MyState.Polling // if error occurred emit Polling State
}
}
.takeUntil {
// stop polling if certain State is reached
}
}

Is it ok to send response to client inside a executeBlocking() handler?

In the vertx route handler below, is it ok to send the response inside the executeBlocking handler?
router.route(HttpMethod.POST, "/some-path").handler(context -> {
vertx.executeBlocking(completePromise -> {
// Do some time-consuming work
context.response().end("the response");
});
Or do I have to do it like below, in the original event loop?
router.route(HttpMethod.POST, "/some-path").handler(context -> {
vertx.executeBlocking(completePromise -> {
// Do some time-consuming work
completePromise.complete();
}, resultHandler -> {
if (resultHandler.succeeded()) {
context.response().setStatusCode(400).end("failed");
} else {
context.response().end("the response");
}
});
});
Both are correct, although it is a better practice to interact with Vert.x API elements in the original event loop. (for safety and performance).

RXJava Observable onNext emitted on different threads

If i try to emit onNext on different thread's, on subscribing it dosent catch the stream on next elements.
public static Observable<Student> getStudents()
{
return Observable.create(e -> {
for(int i : Arrays.asList(1,2,3))
{
Thread t = new Thread(() -> {
e.onNext(new Student("anirba", i));
});
t.start();
}
e.onComplete();
});
}
On sunbscribing to this observable i dont get any responseenter code here
Observable<Student> observer = getStudents();
observer.subscribe(i -> System.out.println(i));
You are creating three threads inside the create method and each is adding an object of Student to stream. The reason why you are not getting any output is all these three threads will be running independently and based on thread scheduler it will be executed. In your case, the onComplete() method might get called before all these three threads add data into the Observable stream. and on-call of onComplete, the stream will be closed and no more data will be accepted by the stream. To make it work just to the below changes, it should work as you are expecting.
public static Observable<Student> getStudents() {
return Observable.create(e -> {
for(int i : Arrays.asList(1,2,3)) {
Thread t = new Thread(() -> {
e.onNext(new Student("anirba", i));
});
t.start();
}
Thread.sleep(1000);
e.onComplete();
});
}
java.util.concurrent.Executor is the right API now for running tasks. To block the current thread you can use CountDownLatch and release it when all tasks are terminated.
ExecutorService executor = Executors.newCachedThreadPool(); // choose the right one
public Observable<Student> getStudents() {
return Observable.<Student>create(emitter -> {
List<Integer> source = Arrays.asList(1, 2, 3);
CountDownLatch latch = new CountDownLatch(source.size());
source
.forEach(i ->
executor.submit(() -> {
emitter.onNext(new Student("anirba", i));
latch.countDown();
}));
latch.await();
emitter.onComplete();
}).serialize();
}
And elsewhere don't forget to call shutdown() on the executor.
You may also add serialize() operator to avoid onnext() calls to overlap.
From the contract :
Observables must issue notifications to observers serially (not in
parallel). They may issue these notifications from different threads,
but there must be a formal happens-before relationship between the
notifications.
For the perpose of testing you can add Thread.sleep(x) to see your loggin. I've already answerd this before here
public static void main(String[] args) throws InterruptedException {
getStudents()
.subscribe(i -> System.out.println(i));
Thread.sleep(2000);
}

What's the usage of the observable emitted by retrywhen() in rxjava2?

I know that if the Observable emits a piece of data, it resubscribes, and if the Observable emits an onError notification, it passes that notification to the observer and terminates. The problem is that if I emit a Obervable.just(1,2),but it won't be accept by the observer.So what's the usage of it?Dose it just tell it to resubscribes,and it's not important what data i emit?
Observable.just(1, "2", 3)
.cast(Integer.class)
.retryWhen(new Function<Observable<Throwable>, ObservableSource<Integer>>() {
#Override
public ObservableSource<Integer> apply(Observable<Throwable> throwableObservable) throws Exception {
return Observable.just(4,5);
}
})
.subscribe(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
Log.i(TAG, "retryWhen重试数据"+integer);
}
});
and the log is
retryWhen重试数据1
retryWhen重试数据1
so Observable.just(4,5) is gone?
You can check out this example from the documentation to better understand how the retryWhen supposed to work (source: http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#retryWhen-io.reactivex.functions.Function-):
Observable.create((ObservableEmitter<? super String> s) -> {
System.out.println("subscribing");
s.onError(new RuntimeException("always fails"));
}).retryWhen(attempts -> {
return attempts.zipWith(Observable.range(1, 3), (n, i) -> i).flatMap(i -> {
System.out.println("delay retry by " + i + " second(s)");
return Observable.timer(i, TimeUnit.SECONDS);
});
}).blockingForEach(System.out::println);
Output is:
subscribing
delay retry by 1 second(s)
subscribing
delay retry by 2 second(s)
subscribing
delay retry by 3 second(s)
subscribing

Mono.subscribe(consumer, errorConsumer, completeConsumer, subscriptionConsumer) does not invoke consumer and completeConsumer?

As subject, the 4th variant of Mono.subscribe does not seem to invoke the success consumer and complete consumer. It only invoke subscription consumer.
The code below failed
CountDownLatch latch = new CountDownLatch(3);
Mono.just(1).subscribe(i -> latch.countDown(), throwable -> {}, latch::countDown, s -> latch.countDown());
boolean success = latch.await(1, TimeUnit.SECONDS);
Assert.assertEquals(true, success);
That's because the versions of subscribe with a Consumer<Subscription> are meant for you to drive the initial request. If you don't call request(n) on the subscription in the consumer, no data will be emitted and the Mono won't complete...
This works as expected:
CountDownLatch latch = new CountDownLatch(3);
Mono.just(1).subscribe(
i -> latch.countDown(),
throwable -> {},
latch::countDown,
s -> {
s.request(Long.MAX_VALUE);
latch.countDown();
});
boolean success = latch.await(1, TimeUnit.SECONDS);
Assert.assertEquals(true, success);

Categories

Resources