How to implement timeout for CompletableFuture
In Java 9+ there is a built-in orTimeout() method:
🟢 Junior Level
In Java 9+ there is a built-in orTimeout() method:
CompletableFuture<String> cf = fetchDataAsync();
// 5 second timeout
cf.orTimeout(5, TimeUnit.SECONDS)
.thenAccept(result -> System.out.println(result))
.exceptionally(ex -> {
if (ex instanceof TimeoutException) {
System.out.println("Timeout!");
}
return "fallback";
});
For Java 8 — you can implement it manually:
public static <T> CompletableFuture<T> withTimeout(
CompletableFuture<T> cf, long timeout, TimeUnit unit
) {
CompletableFuture<T> timeoutCF = new CompletableFuture<>();
// ⚠️ Note: this example creates a new ScheduledThreadPool on each call.
// In production, use a single shared scheduler or don't forget scheduler.shutdown().
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(() ->
timeoutCF.completeExceptionally(new TimeoutException()),
timeout, unit
);
return CompletableFuture.anyOf(cf, timeoutCF)
.thenApply(result -> (T) result);
}
🟡 Middle Level
Java 9+ methods
orTimeout:
cf.orTimeout(5, TimeUnit.SECONDS)
.exceptionally(ex -> {
// TimeoutException
return fallback;
});
completeOnTimeout:
// On timeout — fallback value
cf.completeOnTimeout("default", 5, TimeUnit.SECONDS)
.thenAccept(result -> System.out.println(result));
Java 8 implementation
public static <T> CompletableFuture<T> withTimeout(
CompletableFuture<T> cf, long timeout, TimeUnit unit
) {
CompletableFuture<T> result = new CompletableFuture<>();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// Schedule timeout
ScheduledFuture<?> timeoutFuture = scheduler.schedule(() -> {
result.completeExceptionally(new TimeoutException());
}, timeout, unit);
// Complete normally
cf.whenComplete((value, ex) -> {
timeoutFuture.cancel(false);
if (ex != null) {
result.completeExceptionally(ex);
} else {
result.complete(value);
}
});
return result;
}
Typical mistakes
- Not closing the scheduler: ```java // ❌ Scheduler not closed — memory leak ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// ✅ Close after use scheduler.shutdown();
---
## 🔴 Senior Level
### Internal Implementation
**orTimeout internals:**
```java
// orTimeout registers a whenComplete handler that, on normal
// completion, calls scheduledFuture.cancel(false), cancelling the scheduled task.
// If the timeout fires first — completeExceptionally(TimeoutException)
// completes the CF, and the callback chain no longer executes.
Edge Cases
1. Timeout vs cancellation:
cf.orTimeout(5, TimeUnit.SECONDS);
// Timeout does NOT cancel the task execution!
// The task continues running in the background
// Only the CF completes with TimeoutException
2. Multiple timeouts:
// The last timeout wins (shortest one fires first)
cf.orTimeout(10, TimeUnit.SECONDS)
.orTimeout(5, TimeUnit.SECONDS); // this one fires first
Production Experience
Resilient service:
@Service
public class ResilientService {
public CompletableFuture<Data> getData(String key) {
return fetchDataAsync(key)
.orTimeout(5, TimeUnit.SECONDS)
.exceptionallyCompose(ex -> {
if (ex instanceof TimeoutException) {
return getFromCacheAsync(key);
}
return CompletableFuture.failedFuture(ex);
});
}
}
Best Practices
// ✅ orTimeout for Java 9+
cf.orTimeout(5, TimeUnit.SECONDS);
// ✅ completeOnTimeout for fallback
cf.completeOnTimeout(defaultValue, 5, TimeUnit.SECONDS);
// ✅ Custom timeout for Java 8
withTimeout(cf, 5, TimeUnit.SECONDS);
// ❌ Forgetting to close scheduler
// ❌ Ignoring TimeoutException
🎯 Interview Cheat Sheet
Must know:
- orTimeout(timeout, unit) — Java 9+, completes CF with TimeoutException
- completeOnTimeout(value, timeout, unit) — Java 9+, fallback value on timeout
- Timeout does NOT cancel the task — it continues running in the background
- Java 8: ScheduledExecutorService + anyOf(cf, timeoutCF)
- One shared scheduler for all timeouts, not a new one per call
Common follow-up questions:
- Does orTimeout cancel the task? — No, CF completes with TimeoutException, task continues working
- completeOnTimeout vs orTimeout? — completeOnTimeout returns a fallback, orTimeout throws an exception
- Java 8 timeout without ScheduledExecutor? — No alternative. ScheduledExecutorService is the only option
- Multiple timeouts on one CF? — The last (shortest) one wins
Red flags (DO NOT say):
- “orTimeout cancels task execution” — task continues working
- “Creating a new ScheduledThreadPool per timeout” — memory leak, need one shared scheduler
- “TimeoutException doesn’t need handling” — needs fallback or retry
Related topics:
- [[22. What does orTimeout() method do in Java 9+]]
- [[10. What does anyOf() method do and when is it useful]]
- [[27. How to implement retry logic with CompletableFuture]]
- [[16. How to properly execute multiple parallel requests to microservices]]