More specifically, what are the differences, is any, in these two pieces of code, one returning void
and other returning CompletionStage<Void>
?
void
or CompletionStage<Void>
over the other?x
in thenCompose
. Is there a better way of writing it?boolean
in the function doValidation
and check if true
in performTask
function?CODE SAMPLE 1:
public CompletionStage<Response> performTask(Request request) throws MyException {
doValidation(request);
return service.performTask(request).thenCompose(serviceResponse -> CompletableFuture.completedFuture(buildMyResponse(serviceResponse)));
}
private void doValidation(Request request) throws MyException {
CompletableFuture.runAsync(() -> {
// Validate
if (*someCondition*) {
throw new MyException(.....);
}
});
}
CODE SAMPLE 2:
public CompletionStage<Response> performTask(Request request) throws MyException {
return doValidation(request).thenCompose(x -> {
return service.performTask(request).thenCompose(serviceResponse -> CompletableFuture.completedFuture(buildMyResponse(serviceResponse)));
});
}
private CompletionStage<Void> doValidation(Request request) throws MyException {
return CompletableFuture.runAsync(() -> {
// Validate
if (*someCondition*) {
throw new MyException(.....);
}
});
}
I am addressing your first question
What advantages of using one of void or CompletionStage<Void> over the other?
To understand the difference, you should try logging the flow. It will help you identify how different they both work.
Here is an example similar to the approaches you followed. I came up with my own example because I don't know your code.
public class Test {
public static void main(String[] args) {
tryWithVoid();
// tryWithCompletionStageVoid();
}
public static void tryWithVoid() {
System.out.println("Started");
validate(null);
System.out.println("Exit");
}
public static void tryWithCompletionStageVoid() {
System.out.println("Started");
validateCS(null).thenRun(() -> System.out.println("Validation complete"));
System.out.println("Exit");
}
public static CompletionStage<Void> validateCS(String val) {
return CompletableFuture.runAsync(() -> {
if (val == null) {
System.out.println("Validation failed! value is null");
}
});
}
public static void validate(String val) {
CompletableFuture.runAsync(() -> {
if (val == null) {
System.out.println("Validation failed! value is null");
}
});
}
}
If you run tryWithVoid()
, you will see that the output will be:
Started
Exit
Validation failed! value is null
Whereas, when you run tryWithCompletionStageVoid()
, it will be:
Started
Validation failed! value is null
Validation complete
Exit
In first approach, tryWithVoid()
, it is not waiting for the operation to complete. So the result of the operation may or may not be available for the code following the invocation.
However, in second approach tryWithCompletionStageVoid()
, it waits for the stage to complete before running the next stage.
If you don't have dependency between the stages, you can go with void
The difference is that with returning a void
you cannot do anything chained to the CompletableFuture
after it finishes.
When you return CompletableFuture<Void>
you can chain further things to it. For example invoke thenRun()
, thenRunAsync()
, or simply join()
to wait for it to finish if needed.
So it depends on whether the caller cares about the status of the CompletableFuture
and wants to apply further logic attached to it.
In your case, in Sample 1, doValidation()
is running on a separate thread, and whatever happens to it is really irrelevant. service.performTask()
will take place independently of it, and could even start and/or finish before doValidation()
.
In Sample 2, doValidation()
has to finish successfully before performTask()
actually takes place. So it seems that this version is actually the correct one. Keep in mind that the main thread running CompletionStage<Response> performTask()
will finish and return immediately after setting up the pipeline, the actual performValidation() could still be running (or could even not have been started yet).
By the way you can simplify the call as follows:
service.performTask(request).thenApply(this::buildMyResponse);
You don't have to wrap it in a CompletableFuture
again just to unwrap it again with thenCompose()
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.