Reputation: 129
I am making call to a service, which is returning CompletableFuture.
The output structure is as follows.
Class Output {
public String name;
public Integer age;
}
And I want to make the call to service, and want to go ahead with my execution till the name is present.
Something like,
CompletableFuture<Output> futureOutput = makeServiceCall(input);
String name = futureOutput.get().name;
processName(name); // which will do some costly operations and make use of the name at the end.
In the above approach, I need to wait till my futureOutput
is ready, even though I need it only later.
I look for something like a below approach.
CompletableFuture<Output> futureOutput = makeServiceCall(input);
CompletableFuture<String> futureName = futureOutput.get().name; // This is wrong, but can we create a reference in a similar way?
processName(futureName); // which will do some costly operations and make use of the name at the end.
Its fine for me to change the signature of processName
from String
to CompletableFuture<String>
but not to CompletableFuture<Output>
as Output
does not make any sense for that method.
What are the suggested ways to have a future reference which is a field of another future.
Upvotes: 4
Views: 2387
Reputation: 61148
CompletableFuture.thenApplyAsync
From the JavaDoc:
Returns a new CompletionStage that, when this stage completes normally, is executed using this stage's default asynchronous execution facility, with this stage's result as the argument to the supplied function.
In your example (where T
is the return type of the processName
method):
CompletableFuture<Output> future = makeServiceCall(input);
CompletableFuture<T> result = future.thenApplyAsync(o -> processName(o.name));
Now, when the makeServiceCall
CompletableFuture
completes then another CompletableFuture
is generated to wrap an async call to processName
- this creates an asynchronous pipeline.
Depending on what you want to do, you might what to use CompletableFuture.thenAcceptAsync
instead, for example if processName
doesn't return a useful result:
CompletableFuture<Output> future = makeServiceCall(input);
CompletableFuture<Void> result = future.thenAcceptAsync(o -> processName(o.name));
You would probably also want error handling in case this processing doesn't complete, this can be done with CompletableFuture.exceptionally
. This adds a callback this is invoked if the processing pipeline terminates with an Exception
.
By means for a full example, you could do:
makeServiceCall(input)
.thenApplyAsync(Output::getName)
.thenAcceptAsync(this::processName)
.exceptionally(e -> {
//log the error
System.out.printf("Oops - %s%n", e);
//return some useful default value
return ProcessingResult.SUCCESSFUL;
});
This pipeline (while slightly contrived - there is no need to get the name async) is fully asynchronous. There is no need to block the Thread
that creates the task any any point; the task will either complete successfully or the failure handler will be invoked.
Upvotes: 7
Reputation: 328608
You can supply the result to a new completion stage, for example:
CompletableFuture<Output> futureOutput = makeServiceCall(input);
futureOutput.thenAcceptAsync(output -> processName(output.name));
(or using thenAccept
if you want to block until the operation completes).
Upvotes: 0