Question 21 · Section 19

How to implement timeout for CompletableFuture

In Java 9+ there is a built-in orTimeout() method:

Language versions: English Russian Ukrainian

🟢 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

  1. 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]]