The following example is taken from Spring' Getting Started Creating Asynchronous Methods.
@Service
public class GitHubLookupService {
@Async
public CompletableFuture<User> findUser(String user) throws InterruptedException {
logger.info("Looking up " + user);
String url = String.format("https://api.github.com/users/%s", user);
User results = restTemplate.getForObject(url, User.class);
// Artificial delay of 1s for demonstration purposes
Thread.sleep(1000L);
return CompletableFuture.completedFuture(results);
}
}
AFAIK my knowledge of async methods in computer science goes - It should return immediately. It should be non-blocking.
So lets say somewhere in Spring lets say my code findUser()
is called like so:
CompletableFuture<User> user = service.findUser("foo");
This would actually block. It would block a different thread on the Executor
service but it would block due to the Thread.sleep(1000L)
. Correct ?
So how is this async ?
I mean the whole point of CompletableFuture
is to get a reference to a computation that will be completed in the future. But here when I get back the completed future the computation is already over, ie. we are using CompletableFuture.completedFuture(results)
.
So what is the point of having a CompletableFuture
in this case ? I mean if I am going to block and return only when my computation is over and I have the results, I might as well just return the result and not the CompletableFuture
.
How is this truly non-blocking/async ?
The only non-blocking aspect I find here is offload to a different thread, nothing else.
Am I going wrong somewhere ? What am I missing ?
Thanks.
The problem lies in the way you create your Future
. The code you use is
CompletableFuture.completedFuture(results)
Quoting from the JavaDoc, this is only a wrapper between sync and async, where the computation was done synchronously:
Returns a new CompletableFuture that is already completed with the given value.
This is useful in certain situations where you only want to do asynchronous work for some inputs. Consider
(x) -> x==0 ? CompletableFuture.completedFuture(0) : CompletableFuture.supplyAsync(expensiveComputation)
I hope this makes the difference clear - if you want truly async computations, you need to use the supplyAsync
function:
Returns a new
CompletableFuture
that is asynchronously completed by a task running in theForkJoinPool.commonPool()
with the value obtained by calling the givenSupplier
.