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).
Related
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
}
}
I have two queries that I'm trying to fetch from a DB. I want to perform A and waiting for it to completely finish and then perform B. I don't need to results from A.
Currently I have gotten them to run one after another by nesting the second query after the first. Is there a way to do it without nesting them?
mainRepository.getAppointments().thenAccept(retrievedAppointments -> {
Platform.runLater(() -> {
//Update ui for query A
});
mainRepository.getCustomers().thenAccept(customers -> {
Platform.runLater(() -> {
//Update ui for query B
});
});
public CompletableFuture<ObservableList<Appointment>> getAppointments(){
return CompletableFuture.supplyAsync(() -> {
return queryAppointments();
});
}
public CompletableFuture<ObservableList<Customer>> getCustomers(){
return CompletableFuture.supplyAsync(() -> {
return queryCustomers();
});
}
There are a lot of method available in the CompletableFuture class similar to what you're expecting from nodeJs then. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html
I don't know your exact use case but one approach you could take is to change the thenApply or thenRun. This may not be the cleanest solution because I am not 100% familiar with your use case.
mainRepository.getAppointments().thenAccept(retrievedAppointments -> {
Platform.runLater(() -> {
// update ui for query A
});
}).thenRun(() -> {
mainRepository.getCustomers().thenAccept(customers -> {
Platform.runLater(() -> {
//Update ui for query B
});
}).thenRun(() -> {
// do something else
});
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()));
});
Trying to get lastLocation and once it's done call api. But somehow once location is obtained my api calls always running in mainThread, so i'm getting exception:
android.io.NetworkOnMainThreadException
Here is my location observer:
fun getLocation(): Single<Location> {
return Single.create<Location> { subscriber ->
fusedLocationClient.lastLocation.addOnSuccessListener {
if (it != null) {
subscriber.onSuccess(it)
} else {
subscriber.onError(Exception("No location"))
}
}
}
}
Code that does some transformations
val locationObserver = getLocation()
observables.add(locationObserver.flatMap { _ -> sendDataToServer(data)})
Observer
Single.zip(observables) { args1 -> args1 }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe({
Timber.i("Success")
}, {
Timber.i("Error %s", observables.size, it.localizedMessage)
it.printStackTrace()
})
I've set subscribeOn so it shouldn't be on mainThread, but looks like something missed.
Found that if i will use something like Single.just("One").flatMap{ ... } that will work fine and will be executed on non-main thread.
Is there something to do with getLocation function?
The order of subscribeOn, observeOn, subscribe, and transformations matters. Apparently, it's needed to do the transformations, in this case, the flatMap after specifying the observer thread with observeOn to make sure the code is executed in the right thread.
Trying to get up to speed with RxJava. I have a network call that returns no data. The only response is the code (happy path: 200, 4xx otherwise). I want to listen for this response, but all I can find is how to do it with some sort of response object.
#GET
Observable<Response<ResponseBody>> makeHttpCall(#Url String url);
So my RxJava code looks like this:
myRetrofit.makeHttpCall(url)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response<ResponseBody>>() {
#Override
public void onCompleted() {
Timber.d("on completed");
}
#Override
public void onError(Throwable e) {
if (!(e instanceof EOFException)) {
Timber.e(e, "error occurred");
}
}
#Override
public void onNext(Response<ResponseBody> responseBodyResponse) {
Timber.d("on next");
}
});
This works, but it seems like the wrong solution. I don't like how my observer drops into the onError method. My response is a 200, so I'd like to see it in the onNext or onCompleted methods.
I looked into using Completable, but that didn't work at all. I still think that might be the right way to go, however.
What is the best approach here? I'm wondering if the issue simply traces to my use of <Response<ResponseBody>> and whether there is a different type that is more appropriate in this case.
If you only care about the Http code response then something like this should surfice :
Api:
#GET
Single<Response<ResponseBody>> makeHttpCall(#Url String url);
Call:
myRetrofit.makeHttpCall(url)
.subscribeOn(Schedulers.io())
.map(Response::code)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
i -> Timber.d("code " + i),
e -> {
if (!(e instanceof EOFException)) {
Timber.e(e, "error occurred");
}
});
Also note in your original code you pass the Response<ResponseBody> to the Observer on the main thread - interacting with the ResponseBody on this thread will cause a NetworkOnMainThreadException as dealing with the body is considered a IO operation - I know not your desired intention here, but worth noting when you make api calls that require interaction with the body.
Documentation:
http://reactivex.io/RxJava/javadoc/rx/Observable.html#subscribe(rx.functions.Action1,%20rx.functions.Action1,%20rx.functions.Action0)
Subscription subscribe(Action1 onNext,
Action1 onError, Action0 onCompleted)
Subscribes to an Observable and provides callbacks to handle the items it emits and any error or completion notification it issues.
myRetrofit.makeHttpCall(url)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(n->Timber.d("on next"), e->{
if (!(e instanceof EOFException)) {
Timber.e(e, "error occurred");
}
}, ()->Timber.d("on completed"));