How can I run whenComplete of the CompletableFuture in the original thread that CompletableFuture was created in?
// main thread
CompletableFuture
.supplyAsync(() -> {
// some logic here
return null;
}, testExecutorService);
.whenComplete(new BiConsumer<Void, Throwable>() {
#Override
public void accept(Void aVoid, Throwable throwable) {
// run this in the "main" thread
}
});
Expanded example for JavaFx:
button.setOnClick((evt) -> {
// the handler of the click event is called by the GUI-Thread
button.setEnabled(false);
CompletableFuture.supplyAsync(() -> {
// some logic here (runs outside of GUI-Thread)
return something;
}, testExecutorService);
.whenComplete((Object result, Throwable ex) -> {
// this part also runs outside the GUI-Thread
if (exception != null) {
// something went wrong, handle the exception
Platform.runLater(() -> {
// ensure we update the GUI only on the GUI-Thread
label.setText(ex.getMessage());
});
} else {
// job finished successfull, lets use the result
Platform.runLater(() -> {
label.setText("Done");
});
}
Platform.runLater(() -> {
button.setEnabled(true); // lets try again if needed
});
});
});
This is not the best code you could write in this situation, but it should bring the point across.
Related
I am trying to compose a chain of steps such that I can avoid a large nested chain of if and else calls by creating methods that return CompletableFuture<Boolean> in a manner such as....
client.connect(identifier).thenCompose(b -> client.authenticate())
.thenCompose(b -> client.sendSetting(settings))
.thenCompose(b -> client.saveSettings())
.thenCompose(b -> client.sendKey(key))
.thenCompose(b -> client.setBypassMode(true))
.thenCompose(b -> client.start())
.whenComplete((success, ex) -> {
if(ex == null) {
System.out.println("Yay");
} else {
System.out.println("Nay");
}
});
If the client methods return a CompletableFuture<Boolean> deciding whether to continue processing has to be done in each lambda in the chain and doesn't provide a method to abort early if one of the calls fail. I would rather have the calls return CompletableFuture<Void> and use Exceptions to control if 1) each successive step in the chain executes and 2) final determination of success of the full chain.
I am having trouble finding which method on CompletableFuture<Void> to swap for thenCompose to make things work (let alone compile).
public class FutureChaings {
public static CompletableFuture<Void> op(boolean fail) {
CompletableFuture<Void> future = new CompletableFuture<Void>();
System.out.println("op");
Executors.newScheduledThreadPool(1).schedule(() -> {
if(fail) {
future.completeExceptionally(new Exception());
}
future.complete(null);
}, 1, TimeUnit.SECONDS);
return future;
}
public static void main(String[] args) {
op(false).thenCompose(b -> op(false)).thenCompose(b -> op(true)).whenComplete((b, ex) -> {
if(ex != null) {
System.out.println("fail");
} else {
System.out.println("success");
}
});
}
}
I was able to contrive an example that behaved the way I wanted. So I know what calls to put together to get what I want. Now to figure out what the compiler doesn't like in my real code. Thanks for the comments.
I have a library xyz that gives me a CompletableFuture which I want to process on my Vertx (v3.5) event loop. Currently I am using CompletableFuture.handle(BiFunction) (see below) but I would like to use CompletableFuture.handleAsync(BiFunction, Executor), but I cannot figure out how to provide the vertx event loop thread to the second parameter in this method call.
I tried executing this whole code in Vertx.runOnContext() but the calls inside handleAsync still executed on Java's ForkJoin pool which I want to avoid.
CompletableFuture<Void> f = xyz.someMethod();
f.handle((v, th) -> { //Want to run this in handleAsync()
if (th == null) {
future.complete(null);
} else {
future.completeExceptionally(th);
}
return null;
});
You can achieve this by simply using vertx.nettyEventLoopGroup() as the second parameter, so your code will be something like this:
CompletableFuture<Void> f = xyz.someMethod();
f.handleAsync((v, th) -> {
if (th == null) {
future.complete(null);
} else {
future.completeExceptionally(th);
}
return null;
}, vertx.nettyEventLoopGroup());
Note: the above code will may run the callback code not on the same thread the Vertx future was running.
To preserve the threading model of the Vertx you need to use the following code:
CompletableFuture<String> f = xyz.someMethod();
Context context = vertx.getOrCreateContext();
f.handleAsync((v, th) -> {
context.runOnContext(event -> {
if (th == null) {
future.complete(null);
} else {
future.completeExceptionally(th);
}
});
return null;
});
This can be done the following way. It is a workaround, not an optimal solution. Initially I placed the runOnContext call outside handle method, which is why it did not work.
CompletableFuture<Void> f = xyz.someMethod();
f.handle((v, th) -> {
vertx.runOnContext(e->{
if (th == null) {
future.complete(null);
} else {
future.completeExceptionally(th);
}
});
return null;
});
This will make one extra context switch (from ForkJoin pool to vertx event loop) which is one disadvantage of this approach.
In RxJava2 you can't do something like this
observable.subscribe(s -> {
System.out.println(s);
}, e -> {
System.err.println(e);
}, c -> {
System.err.println(c);
});
because c-> {} is not Action type(obviously)
Instead you're forced to do something like this
Action onComplete = new Action() {
#Override
public void run() throws Exception {
System.out.println("on complete");
}
};
observable.subscribe(s -> {
System.out.println(s);
}, e -> {
System.err.println(e);
},onComplete);
Any reason why onComplete is not made as Consumer type?
The reason is that onComplete doesnt emit any data. A Consumer requires a Type. If something is completed, it's completed.
If you have a Completable it only calls onComplete() which doesnt have emitted data aswell.
onCompleted really means completed. onNext/onSuccess means it has data emitted with Consumer<Type> while onError means it has a Consumer<Throwable>.
I am using RxAndroid to do some stuff in the background. This is my code:
Observable<MyClass[]> observable = Observable.create(new Observable.OnSubscribe<MyClass[]>() {
#Override
public void call(Subscriber<? super MyClass[]> subscriber) {
System.out.println(Looper.myLooper() + " - " + Looper.getMainLooper());
try {
MyObject myObject = ...
//do the background work
subscriber.onNext(myObject);
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
e.printStackTrace();
}
}
});
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<MyClass[]>() {
#Override
public void call(MyClass[] myObjects) {
//do work on the ui Thread
}
}
);
This is my first time using RxAndroid / RxJava / Looper.myLooper() / Looper.getMainLooper()
From what I am told, Looper.myLooper() gives you the name ID of the thread the current code is running on and Looper.getMainLooper() gives you the ID of the main Thread. When I run the app, in the SysOut, it prints out the same id for both of them.
Am I doing something wrong or am I misunderstanding the 2 Looper functions?
It's recommended that you don't use Observable.create unless you really know what you are doing with Observables. There are a lot of things that you can potentially get wrong.
The reason your code inside your create is running on the main thread here is that it is being called when the Observable is being created not when you are subscribing to it.
For what you are trying to achieve I would use Observable.defer From the docs:
The Defer operator waits until an observer subscribes to it, and then it generates an Observable, typically with an Observable factory function.
The code would look something like:
Observable<MyObject> observable = Observable.defer(new Func0<Observable<MyObject>>() {
#Override
public Observable<MyObject> call() {
System.out.println(Looper.myLooper() + " - " + Looper.getMainLooper());
try {
MyObject myObject = new MyObject();
return Observable.just(myObject);
}
catch (Exception e) {
return Observable.error(e);
}
}
});
Subscription s = observable
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Action1<MyObject>() {
#Override
public void call(MyObject myObject) {
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
}
);
Now in your logcat you will get:
I/System.out: null - Looper (main, tid 1) {5282c4e0}
The reason the Looper.myLooper() function returns null is that when you create a new thread unless you call Looper.prepare() the thread will not have a looper. You generally don't need a looper on a thread unless you want to post a Runnable to it anyhow.
I'm experimenting with RxJava and Java 8's CompletableFuture class
and do not quite get how to handle timeout conditions.
import static net.javacrumbs.futureconverter.java8rx.FutureConverter.toObservable;
// ...
Observable<String> doSomethingSlowly() {
CompletableFuture<PaymentResult> task = CompletableFuture.supplyAsync(() -> {
// this call may be very slow - if it takes too long,
// we want to time out and cancel it.
return processor.slowExternalCall();
});
return toObservable(task);
}
// ...
doSomethingSlowly()
.single()
.timeout(3, TimeUnit.SECONDS, Observable.just("timeout"));
This basically works (if the timeout of three seconds is reached, "timeout" is published). I would however additionally want to cancel the future task that I've wrapped in an Observable - is that possible with an RxJava-centric approach?
I know that one option would be to handle the timeout myself, using task.get(3, TimeUnit.SECONDS), but I wonder if it's possible to do all task handling stuff in RxJava.
Yes, you can do this. You would add a Subscription to the Subscriber.
This allows you to listen in on unsubscriptions, which will happen if you explicitly call subscribe().unsubscribe() or if the Observable completes successfully or with an error.
If you see an unsubscription before the future has completed, you can assume it's because of either an explicit unsubscribe or a timeout.
public class FutureTest {
public static void main(String[] args) throws IOException {
doSomethingSlowly()
.timeout(1, TimeUnit.SECONDS, Observable.just("timeout"))
.subscribe(System.out::println);
System.in.read(); // keep process alive
}
private static Observable<String> doSomethingSlowly() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
return "Something";
});
return toObservable(future);
}
private static <T> Observable<T> toObservable(CompletableFuture<T> future) {
return Observable.create(subscriber -> {
subscriber.add(new Subscription() {
private boolean unsubscribed = false;
#Override
public void unsubscribe() {
if (!future.isDone()){
future.cancel(true);
}
unsubscribed = true;
}
#Override
public boolean isUnsubscribed() {
return unsubscribed;
}
});
future.thenAccept(value -> {
if (!subscriber.isUnsubscribed()){
subscriber.onNext(value);
subscriber.onCompleted();
}
}).exceptionally(throwable -> {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(throwable);
}
return null;
});
});
}
}