Implementing Async Processing to Improve API Response Time
Fast recall
Quick revision
Core answer
Whether you understand the difference between async execution and async I/O, and can apply the right technique — @Async, CompletableFuture, or message queues — for different scenarios.
Using @Async for everything including database calls, which does not make them non-blocking — just moves the blocking to a different thread, wasting thread pool capacity.
Distinguish fire-and-forget from async-and-wait patterns, explain when to use a message queue instead, and mention @Async exception handling via AsyncUncaughtExceptionHandler.
Interview-ready guide
Interview answer
Async processing separates two concerns that developers often conflate: accepting a request and completing the work triggered by that request. The right async pattern depends on whether the work needs to complete before responding or can happen after.
Pattern 1 — Parallel fan-out for independent I/O
- When a request requires data from multiple independent sources (three downstream services, two database queries), run them in parallel instead of sequentially
CompletableFutureanduser = CompletableFuture.supplyAsync(() -> userService.get(id), executor) CompletableFuture;orders = CompletableFuture.supplyAsync(() -> orderService.get(id), executor) CompletableFuture.allOf(user, orders).join()waits for both- Which means three 100ms calls take 100ms instead of 300ms; use a dedicated executor with a bounded queue to prevent unbounded thread creation
Pattern 2 — Fire-and-forget with @Async
- For work that doesn't affect the API response (send a welcome email, update analytics, write an audit log):
@Asyncon the method executes it in a separate thread after the calling method returns - Configure a dedicated thread pool:
@EnableAsync+ThreadPoolTaskExecutorwithmaxPoolSize,queueCapacity, andthreadNamePrefix; without custom configuration,@Asyncuses Spring's default thread pool which can starve other async tasks
Pattern 3 — Message queue for durability
- When the async work must be completed eventually, even if the application restarts: publish the work item to a message queue (SQS, Kafka, RabbitMQ); a separate consumer service processes it durably
- Which means the API responds immediately after publishing to the queue (milliseconds), and the work is guaranteed to be processed even if the consumer crashes — it retries from the queue
Pattern 4 — WebFlux for non-blocking I/O
- Spring WebFlux with Project Reactor provides non-blocking I/O — the thread is released while waiting for I/O to complete, allowing it to handle other requests
Monoreturns a Mono that doesn't block a thread while waiting; useful when handling thousands of concurrent I/O-heavy requestsuser = webClient.get().uri(url).retrieve().bodyToMono(UserInfo.class)
Implementing Async Processing to Improve API Response Time
Direct Answer
Use @Async with CompletableFuture for parallel I/O fan-out, message queues for fire-and-forget background work, and virtual threads or WebFlux for high-concurrency I/O-bound scenarios.
Common Mistakes & Pitfalls
Using @Async for everything including database calls, which does not make them non-blocking — just moves the blocking to a different thread, wasting thread pool capacity.
Check your understanding
Answer these without looking back at the article.
- How do you propagate MDC trace context to @Async threads in Spring Boot?
- When should you use a message queue instead of @Async for background processing?
- How does @Async with CompletableFuture differ from Project Reactor's Mono.zip()?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.