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?

+3


source to share


4 answers


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()

.

+3


source


Try the following:



public List<Provider> get() {
    List<Provider> providers = Collections.synchronizedList(new ArrayList<>());
    CompletableFuture<List<Provider>> providersResponse = getSomeData();
    providersResponse.thenAccept(providers::addAll);
    return providers;
}

      

+2


source


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;
}

      

+1


source


Try the following:

public List<Provider> get() {
    return getSomeData().get();
}

      

-1


source







All Articles