The community asks me to do some experiments, so I made them. I found that the answer to my question is simple: @Cacheable and @Async do not work together if they are placed above the same method.
To be clear, I was not asking for a way to directly make the cache return the object owned by a CompletableFuture. This is impossible, and if it isn't so, it will break the contract of asynchronous computation of the CompletableFuture class.
As I said, the two annotations do not work together on the same method. If you think about it, it is obvious. Marking with @Async is also @Cacheable means to delegate the whole cache management to different asynchronous threads. If the computation of the value of the CompletableFuture will take a long time to complete, the value in the cache will be placed after that time by Spring Proxy.
Obviously, there is a workaround. The workaround uses the fact the CompletableFuture is a promise. Let's have a look at the code below.
@Component
public class CachedService {
    /* Dependecies resolution code */
    private final AsyncService service;
    @Cacheable(cacheNames = "ints")
    public CompletableFuture<Integer> randomIntUsingSpringAsync() throws InterruptedException {
        final CompletableFuture<Integer> promise = new CompletableFuture<>();
        // Letting an asynchronous method to complete the promise in the future
        service.performTask(promise);
        // Returning the promise immediately
        return promise;
    }
}
@Component
public class AsyncService {
    @Async
    void performTask(CompletableFuture<Integer> promise) throws InterruptedException {
        Thread.sleep(2000);
        // Completing the promise asynchronously
        promise.complete(random.nextInt(1000));
    }
}
The trick is to create an incomplete promise and return it immediately from the method marked with the @Cacheable annotation. The promise will be completed asynchronously by another bean that owns the method marked with the @Async annotation.
As a bonus, I also implemented a solution that does not use the Spring @Async annotation, but it uses the factory methods available in the CompletableFuture class directly.
@Cacheable(cacheNames = "ints1")
public CompletableFuture<Integer> randomIntNativelyAsync() throws
        InterruptedException {
    return CompletableFuture.supplyAsync(this::getAsyncInteger, executor);
}
private Integer getAsyncInteger() {
    logger.info("Entering performTask");
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return random.nextInt(1000);
}
Anyway, I shared the complete solution to my GitHub problem, spring-cacheable-async.
Finally, the above is a long description of what the Jira SPR-12967 refers to.
I hope it helps.
Cheers.