title: Try waiting for a CompletableFuture
body: <p>Is there a way of trying to wait for a <code>CompletableFuture</code> a certain amount of time before giving a different result back <em>without</em> cancelling the future after timing out?</p>
  
  <p>I have a service (let's call it <code>expensiveService</code>) that runs off to do its own thing. It returns a result:</p>
  
  <pre><code>enum Result {
      COMPLETED,
      PROCESSING,
      FAILED
  }
  </code></pre>
  
  <p>I'm willing to [block and] wait for it for a short amount of time (let's say 2 s). If it doesn't finish, I want to return a different result, but I want the service to carry on doing its own thing. It would be the client's job to then inquire as to whether the service is finished or not (e.g. through websockets or whatever).</p>
  
  <p>I.e. we have the following cases:</p>
  
  <ul>
  <li><code>expensiveService.processAndGet()</code> takes 1 s and completes its future. It returns <code>COMPLETED</code>.</li>
  <li><code>expensiveService.processAndGet()</code> fails after 1 s. It returns <code>FAILED</code>.</li>
  <li><code>expensiveService.processAndGet()</code> takes 5 s and completes its future. It returns <code>PROCESSING</code>. If we ask another service for the result, we get <code>COMPLETED</code>.</li>
  <li><code>expensiveService.processAndGet()</code> fails after 5 s. It returns <code>PROCESSING</code>. If we ask another service for the result, we get <code>FAILED</code>.</li>
  </ul>
  
  <p>In this specific case, we actually need to fetch the current result object anyway on a timeout, resulting in the following additional edge-case. This causes some issues with the solutions suggested below:</p>
  
  <ul>
  <li><code>expensiveService.processAndGet()</code> takes 2.01 s and completes its future. It returns either <code>PROCESSING</code> or <code>COMPLETED</code>.</li>
  </ul>
  
  <p>I'm also using Vavr and am open to suggestions using Vavr's <code>Future</code>.</p>
  
  <p>We have created three possible solutions which all have their own positives and negatives:</p>
  
  <h2>#1 Wait for another Future</h2>
  
  <pre><code>CompletableFuture&lt;Result&gt; f = expensiveService.processAndGet();
  return f.applyToEither(Future.of(() -&gt; {
              Thread.sleep(2000);
              return null;
          }).map(v -&gt; resultService.get(processId)).toCompletableFuture(),
          Function.identity());
  </code></pre>
  
  <h3>Problems</h3>
  
  <ol>
  <li>The second <code>resultService</code> is always called.</li>
  <li>We take up the entire Thread for 2 s.</li>
  </ol>
  
  <h2>#1a Wait for another Future that checks the first Future</h2>
  
  <pre><code>CompletableFuture&lt;Result&gt; f = expensiveService.processAndGet();
  return f.applyToEither(Future.of(() -&gt; {
              int attempts = 0;
              int timeout = 20;
              while (!f.isDone() &amp;&amp; attempts * timeout &lt; 2000) {
                  Thread.sleep(timeout);
                  attempts++;
              }
              return null;
          }).map(v -&gt; resultService.get(processId)).toCompletableFuture(),
          Function.identity());
  </code></pre>
  
  <h3>Problems</h3>
  
  <ol>
  <li>The second <code>resultService</code> is still always called.</li>
  <li>We need to pass the first Future to the second, which isn't so clean.</li>
  </ol>
  
  <h2>#2 <code>Object.notify</code></h2>
  
  <pre><code>Object monitor = new Object();
  CompletableFuture&lt;Upload&gt; process = expensiveService.processAndGet();
  synchronized (monitor) {
      process.whenComplete((r, e) -&gt; {
          synchronized (monitor) {
              monitor.notifyAll();
          }
      });
      try {
          int attempts = 0;
          int timeout = 20;
          while (!process.isDone() &amp;&amp; attempts * timeout &lt; 2000) {
              monitor.wait(timeout);
              attempts++;
          }
      } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
      }
  }
  if (process.isDone()) {
      return process.toCompletableFuture();
  } else {
      return CompletableFuture.completedFuture(resultService.get(processId));
  }
  </code></pre>
  
  <h3>Problems</h3>
  
  <ol>
  <li>Complex code (potential for bugs, not as readable).</li>
  </ol>
  
  <h2>#3 Vavr's <code>Future.await</code></h2>
  
  <pre><code>return Future.of(() -&gt; expensiveService.processAndGet()
          .await(2, TimeUnit.SECONDS)
          .recoverWith(e -&gt; {
              if (e instanceof TimeoutException) {
                  return Future.successful(resultService.get(processId));
              } else {
                  return Future.failed(e);
              }
          })
          .toCompletableFuture();
  </code></pre>
  
  <h3>Problems</h3>
  
  <ol>
  <li>Needs a Future in a Future to avoid <code>await</code> cancelling the inner Future.</li>
  <li>Moving the first Future into a second breaks [legacy] code that relies on <code>ThreadLocal</code>s.</li>
  <li><code>recoverWith</code> and catching the <code>TimeoutException</code> isn't that elegant.</li>
  </ol>
  
  <h2>#4 <code>CompletableFuture.orTimeout</code></h2>
  
  <pre><code>return expensiveService.processAndGet()
          .orTimeout(2, TimeUnit.SECONDS)
          .&lt;CompletableFuture&lt;Upload&gt;&gt;handle((u, e) -&gt; {
              if (u != null) {
                  return CompletableFuture.completedFuture(u);
              } else if (e instanceof TimeoutException) {
                  return CompletableFuture.completedFuture(resultService.get(processId));
              } else {
                  return CompletableFuture.failedFuture(e);
              }
          })
          .thenCompose(Function.identity());
  </code></pre>
  
  <h3>Problems</h3>
  
  <ol>
  <li>Although in my case the <code>processAndGet</code> future is not cancelled, according to docs, it should be.</li>
  <li>The exception handling is not nice.</li>
  </ol>
  
  <h2>#5 <code>CompletableFuture.completeOnTimeout</code></h2>
  
  <pre><code>return expensiveService.processAndGet()
          .completeOnTimeout(null, 2, TimeUnit.SECONDS)
          .thenApply(u -&gt; {
              if (u == null) {
                  return resultService.get(processId);
              } else {
                  return u;
              }
          });
  </code></pre>
  
  <h3>Problems</h3>
  
  <ol>
  <li>Although in my case the <code>processAndGet</code> future is not completed, according to docs, it should be.</li>
  <li>What if <code>processAndGet</code> wanted to return <code>null</code> as a different state?</li>
  </ol>
  
  <p>All of these solutions have disadvantages and require extra code but this feels like something that should be supported either by <code>CompletableFuture</code> or Vavr's <code>Future</code> out-of-the-box. Is there a better way to do this?</p>