Applying CompletableFuture for Asynchronous Programming in Java: A ZhiZhi Store Detail Page Case Study
This article explains the limitations of Java Future, introduces CompletableFuture's asynchronous and compositional capabilities, and demonstrates how to refactor a store detail page service using CompletableFuture, custom thread pools, and design patterns to achieve parallel execution, reduced latency, and better code maintainability.
1 Background
ZhiZhi store detail pages provide ordering and reservation functions on mini‑programs and apps. Initially the service handled low QPS with a simple serial implementation, but as the page grew richer the serial approach caused high latency (up to 5 seconds) and tight coupling.
Each module (basic product info, discount info, store info, inspection report, standard parameters, and assembly) takes 0.5–1 second. Executing them sequentially exceeds acceptable response times, while parallel execution could finish within about 2 seconds.
Java 8 introduced java.util.concurrent.CompletableFuture, extending Future and CompletionStage to support asynchronous multi‑threaded tasks.
2 Evolution of Future and CompletableFuture
2.1 Future Overview
The Future interface (in java.util.concurrent) provides three core abilities: cancel a running task, check if a task is done, and retrieve the task result.
package java.util.concurrent;<br/>public interface Future<V> {<br/> // Cancel execution<br/> boolean cancel(boolean mayInterruptIfRunning);<br/> // Check cancellation status<br/> boolean isCancelled();<br/> // Check completion status<br/> boolean isDone();<br/> // Retrieve result (may block)<br/> V get() throws InterruptedException, ExecutionException;<br/> // Retrieve result with timeout<br/> V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;<br/>}2.2 Limitations of Future
Blocking the main thread while waiting for results, leading to unnecessary CPU consumption.
No support for chaining dependent tasks.
Cannot combine results from multiple tasks.
Lacks built‑in exception handling.
These drawbacks motivated the introduction of CompletableFuture in JDK 8, which enables non‑blocking, chainable, and composable asynchronous programming.
3 CompletableFuture
CompletableFutureimplements Future and CompletionStage, offering functional‑style callbacks, result transformation, and task composition.
3.1 Capabilities
Besides the traditional get(), CompletableFuture provides:
3.1.1 Creating Asynchronous Tasks
public static CompletableFuture<Void> runAsync(Runnable runnable)<br/>public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)<br/>public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)<br/>public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) runAsynccreates a task without a return value, while supplyAsync creates one with a result, both defaulting to ForkJoinPool.commonPool() unless a custom executor is supplied.
3.1.2 Asynchronous Callbacks
// Called when the previous stage finishes, receives result or exception<br/>CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)<br/>// Transforms result or exception and returns a new stage<br/><U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn)<br/>// Async version of whenComplete<br/>CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)<br/>// Handles exception and provides an alternative result<br/>CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)3.1.3 Task Composition
The CompletionStage interface defines many composition methods; common ones include:
// Transform result asynchronously<br/><U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)<br/>// Consume result without returning a value<br/>CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)<br/>// Wait for all given futures to complete<br/>static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)<br/>// Complete when any future finishes<br/>static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)3.2 Solution for the Store Detail Page
The workflow is split into three phases:
Parallel retrieval of basic product info, inspection report, standard parameters, and store info.
Fetch discount info after phase 1 completes.
Assemble all results once phases 1 and 2 are done.
Implementation uses a custom thread pool and several CompletableFuture instances:
ExecutorService testThreadPool = Executors.newFixedThreadPool(10);<br/>ResultDTO resultDTO = new ResultDTO();<br/><br/>// Basic info<br/>CompletableFuture<Void> productBaseInfoFuture = CompletableFuture.runAsync(() -> {<br/> BaseInfoDTO baseInfoDTO = rpcxx;<br/> resultDTO.setBaseInfoDTO(baseInfoDTO);<br/>}, testThreadPool);<br/><br/>// Discount info (depends on basic info)<br/>CompletableFuture<Void> couponInfoFuture = productBaseInfoFuture.thenAcceptAsync(() -> {<br/> CouponInfoDTO couponInfoDTO = rpcxx;<br/> resultDTO.setCouponInfoDTO(couponInfoDTO);<br/>}, testThreadPool);<br/><br/>// Inspection report<br/>CompletableFuture<Void> qcInfoFuture = CompletableFuture.runAsync(() -> {<br/> QcInfoDTO qcInfoDTO = rpcxx;<br/> resultDTO.setQcInfoDTO(qcInfoDTO);<br/>}, testThreadPool);<br/><br/>// Store info<br/>CompletableFuture<Void> storeInfoFuture = CompletableFuture.runAsync(() -> {<br/> StoreInfoDTO storeInfoDTO = rpcxx;<br/> resultDTO.setStoreInfoDTO(storeInfoDTO);<br/>}, testThreadPool);<br/><br/>// Standard parameters<br/>CompletableFuture<Void> spuInfoFuture = CompletableFuture.runAsync(() -> {<br/> SpuInfoDTO spuInfoDTO = rpcxx;<br/> resultDTO.setSpuInfoDTO(spuInfoDTO);<br/>}, testThreadPool);<br/><br/>// Wait for all parallel tasks<br/>CompletableFuture<Void> allQuery = CompletableFuture.allOf(couponInfoFuture, qcInfoFuture, storeInfoFuture, spuInfoFuture);<br/>// Assemble final result<br/>CompletableFuture<Void> buildFuture = allQuery.thenAcceptAsync((r) -> {<br/> // assembly logic here<br/>}, testThreadPool).join();This approach reduces the overall latency to the longest individual module (≈1 second) and decouples the code.
3.3 Further Refactoring with Factory & Strategy
To avoid repetitive task definitions, each module can be represented as a strategy handler selected via a simple factory. All handlers are executed with CompletableFuture.allOf():
List<String> eventList = Arrays.asList("xx", "xxx");<br/>CompletableFuture.allOf(eventList.stream().map(event -><br/> CompletableFuture.runAsync(() -> {<br/> // obtain handler from factory based on event type<br/> if (handler != null) { /* execute */ }<br/> }, testThreadPool)<br/>).toArray(CompletableFuture[]::new)).join();3.4 Actual Impact
After refactoring the ZhiZhi store detail API with CompletableFuture + Factory + Strategy, the code became loosely coupled, easier to extend, and showed a noticeable reduction in response time in production.
4 Summary
Use CompletableFuture when multiple independent tasks can run in parallel; the more tasks, the greater the benefit.
It supports both sequential chaining and combinatorial patterns (AND/OR).
When collecting results in a shared collection, ensure thread‑safety.
Handle task dependencies and timeouts to avoid blocking the system.
5 References
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Zhuanzhuan Tech
A platform for Zhuanzhuan R&D and industry peers to learn and exchange technology, regularly sharing frontline experience and cutting‑edge topics. We welcome practical discussions and sharing; contact waterystone with any questions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
