Return value directly from CompletableFuture.thenAccept
I am trying to return a list from mine CompletableFuture
like this:
public List<Provider> get() {
CompletableFuture<List<Provider>> providersResponse = getSomeData();
return providersResponse.thenAccept((List<Provider> providers) -> {
return providers;
});
}
It fails with "surprise return". How can I return the result in an asynchronous way?
source to share
There is a fundamental contradiction in your goal. You can only have: get()
return a complete, directly usable list, or "return the result along an asynchronous path".
If a method List<Provider> get()
has to return List
that can be used by the caller without restriction, it cannot remain asynchronous because the operation must complete when it returns get()
. This can be done as simple as calling join()
, waiting for completion, and getting the result:
public List<Provider> get() {
CompletableFuture<List<Provider>> providersResponse = getSomeData();
return providersResponse.join();
}
or simply
public List<Provider> get() {
return getSomeData().join();
}
This effectively turns a potentially asynchronous operation getSomeData()
into a synchronous operation.
This answer using
public List<Provider> get() {
List<Provider> providers = new ArrayList<>();
CompletableFuture<List<Provider>> providersResponse = getSomeData();
providersResponse.thenAccept(providers::addAll);
return providers;
}
does not wait for the operation to complete, but returns a new one ArrayList
after scheduling an operation addAll
, the execution of which has no complete control over this method.
This is an asynchronous operation, so when it get()
returns it List
may still be empty, but will be updated by an arbitrary thread later. Since ArrayList
it is not thread-safe, the perceived state should not be either empty or completed; it can be an arbitrary intermediate state that does not even require agreement. Be prepared for strange exceptions, impossible situations or other surprises when using this code.
When you fix this code with thread safety List
, you still have the problem that the return List
can be empty when requested and filled at an arbitrary point in time outside of the control of the callers. What is not fixed, as was said at the beginning, is the main problem for an asynchronous operation, but it returns List
.
CompletableFuture
is already an encapsulation of a potentially asynchronous operation, so if you want the operation to remain asynchronous, simply return to the CompletableFuture<List<Provider>>
caller to gain control. Otherwise, if you want the caller to get List
one that can be used without restriction, wait for completion before returning the result, eg. through join()
.
source to share
From the Java doc , thenAccept
return void, so you can't use it to return a list of providers:
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
To solve this problem:
public List<Provider> get() {
//Create a List or providers
List<Provider> listProviders = new ArrayList<>();
CompletableFuture<List<Provider>> providersResponse = getSomeData();
providersResponse.thenAccept((t) -> {
//action in your List
});
//return this list
return listProviders;
}
source to share