Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return value directly from CompletableFuture.thenAccept

I am trying to return a list from my CompletableFuture like this:

public List<Provider> get() {
    CompletableFuture<List<Provider>> providersResponse = getSomeData();
    return providersResponse.thenAccept((List<Provider> providers) -> {
        return providers;
    });
}

It fails with "unexpected return type though. How can I return the result in an async way?

like image 276
span Avatar asked Apr 19 '17 07:04

span


People also ask

What does CompletableFuture return?

The most generic way to process the result of a computation is to feed it to a function. The thenApply method does exactly that; it accepts a Function instance, uses it to process the result, and returns a Future that holds a value returned by a function: CompletableFuture<String> completableFuture = CompletableFuture.

How do you end a Completable Future?

As such, there's nothing you can do through CompletableFuture to interrupt any thread that may be running some task that will complete it. You'll have to write your own logic which tracks any Thread instances which acquire a reference to the CompletableFuture with the intention to complete it.

How does CompletableFuture supplyAsync work?

CompletableFuture executes these tasks in a thread obtained from the global ForkJoinPool. commonPool(). But hey, you can also create a Thread Pool and pass it to runAsync() and supplyAsync() methods to let them execute their tasks in a thread obtained from your thread pool.

What is CompletableFuture void?

CompletableFuture<Void> : The Void tells the user there is no result to be expected.


2 Answers

There is a fundamental contradiction in your goal. You can have only either, get() returning a complete, directly usable list or “return the result in an async way”.

If the method List<Provider> get() is supposed to return a List which can be used by the caller without restrictions, it can’t stay an asynchronous operation, as the operation must have been completed when get() returns. This can be achieved as simple as calling join(), to wait for the completion and get the result:

public List<Provider> get() {
    CompletableFuture<List<Provider>> providersResponse = getSomeData();
    return providersResponse.join();
}

or just

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

This effectively turns the potentially asynchronous operation of 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 completion of the operation, but returns a new ArrayList after scheduling an addAll operation, whose execution is entirely out of control of this method.

This is an asynchronous operation, so when get() returns, the List might still be empty, but get updated at a later time by an arbitrary thread. Since ArrayList is not thread safe, the perceived state doesn’t have to be either, empty or completed, it may be an arbitrary in-between state, not even required to be consistent. Be prepared for strange exceptions, impossible-looking situations or other surprises when using that code.

When you fix that code by using a thread safe List implementation, you still have the problem that the returned List might be empty when queried and get filled at an arbitrary point of time outside of the caller’s control. That’s not fixable, as said at the beginning, it’s the fundamental problem of wanting an asynchronous operation but returning a List.

The CompletableFuture is already an encapsulation of a potentially asynchronous operation, so if you want the operation to stay asynchronous, just return the CompletableFuture<List<Provider>> to the caller, to allow to gain control. Otherwise, if you want the caller to receive a List which can be used without restrictions, wait for the completion before returning the result, e.g. via join().

like image 124
Holger Avatar answered Oct 28 '22 05:10

Holger


Try this:

public List<Provider> get() {
    List<Provider> providers = Collections.synchronizedList(new ArrayList<>());
    CompletableFuture<List<Provider>> providersResponse = getSomeData();
    providersResponse.thenAccept(providers::addAll);
    return providers;
}
like image 22
Darshan Mehta Avatar answered Oct 28 '22 07:10

Darshan Mehta