尝试等待CompletableFuture

问题描述 投票:1回答:1

是否有一种方法可以尝试等待CompletableFuture一定时间后再返回不同的结果在超时后取消将来?

我有一项服务(我们称其为expensiveService),该服务会自行运行。返回结果:

enum Result {
    COMPLETED,
    PROCESSING,
    FAILED
}

我愿意[阻止并等待]一小段时间(比如说2 s)。如果还没有完成,我想返回一个不同的结果,但是我希望服务继续做自己的事情。然后查询服务是否完成(例如通过websockets或其他方法)是客户的工作。

即我们有以下几种情况:

  • expensiveService.processAndGet()需要1 s并完成其未来。返回COMPLETED
  • expensiveService.processAndGet()在1秒钟后失败。返回FAILED
  • expensiveService.processAndGet()需要5秒钟并完成其未来。返回PROCESSING。如果我们要求其他服务提供结果,则会得到COMPLETED
  • expensiveService.processAndGet()在5秒钟后失败。返回PROCESSING。如果我们要求其他服务提供结果,则会得到FAILED

在这种特定情况下,实际上我们无论如何都需要在超时时获取当前结果对象,从而导致以下额外的边缘情况。这会导致以下建议的解决方案出现一些问题:

  • expensiveService.processAndGet()耗时2.01秒,并完成了未来。它返回PROCESSINGCOMPLETED

我也在使用Vavr,并愿意使用Vavr的Future提出建议。

我们创建了三种可能的解决方案,它们都有各自的积极和消极:

#1等待另一个未来

CompletableFuture<Result> f = expensiveService.processAndGet();
return f.applyToEither(Future.of(() -> {
            Thread.sleep(2000);
            return null;
        }).map(v -> resultService.get(processId)).toCompletableFuture(),
        Function.identity());

问题

  1. 总是调用第二个resultService
  2. 我们占用整个线程2秒钟。

#1a等待另一个检查第一个Future的Future

CompletableFuture<Result> f = expensiveService.processAndGet();
return f.applyToEither(Future.of(() -> {
            int attempts = 0;
            int timeout = 20;
            while (!f.isDone() && attempts * timeout < 2000) {
                Thread.sleep(timeout);
                attempts++;
            }
            return null;
        }).map(v -> resultService.get(processId)).toCompletableFuture(),
        Function.identity());

问题

  1. 第二个resultService仍然总是被调用。
  2. 我们需要将第一个Future传递给第二个,不是那么干净。

#2 Object.notify

Object monitor = new Object();
CompletableFuture<Upload> process = expensiveService.processAndGet();
synchronized (monitor) {
    process.whenComplete((r, e) -> {
        synchronized (monitor) {
            monitor.notifyAll();
        }
    });
    try {
        int attempts = 0;
        int timeout = 20;
        while (!process.isDone() && attempts * timeout < 2000) {
            monitor.wait(timeout);
            attempts++;
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}
if (process.isDone()) {
    return process.toCompletableFuture();
} else {
    return CompletableFuture.completedFuture(resultService.get(processId));
}

问题

  1. 复杂的代码(潜在的错误,难以理解)。>>
  2. #3 Vavr的Future.await

return Future.of(() -> expensiveService.processAndGet()
        .await(2, TimeUnit.SECONDS)
        .recoverWith(e -> {
            if (e instanceof TimeoutException) {
                return Future.successful(resultService.get(processId));
            } else {
                return Future.failed(e);
            }
        })
        .toCompletableFuture();

问题

  1. 需要一个将来的将来以避免await取消内部将来。
  2. 将第一个Future转换为第二个将打破依赖ThreadLocal的[旧版]代码。
  3. [recoverWith和捕捉TimeoutException不太好。
  4. #4 CompletableFuture.orTimeout

return expensiveService.processAndGet()
        .orTimeout(2, TimeUnit.SECONDS)
        .<CompletableFuture<Upload>>handle((u, e) -> {
            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());

问题

  1. 尽管在我看来,processAndGet的未来并没有被取消,但它应该是。
  2. 异常处理不好。
  3. #5 CompletableFuture.completeOnTimeout

return expensiveService.processAndGet()
        .completeOnTimeout(null, 2, TimeUnit.SECONDS)
        .thenApply(u -> {
            if (u == null) {
                return resultService.get(processId);
            } else {
                return u;
            }
        });

问题

  1. 尽管在我看来,processAndGet的未来还没有完成,但是根据文档,它应该是。
  2. 如果processAndGet要返回null作为其他状态怎么办?
  3. 所有这些解决方案都有缺点,并且需要额外的代码,但是感觉像CompletableFuture或Vavr的Future都应立即提供支持。有更好的方法吗?

有没有一种方法可以等待CompletableFuture一定的时间,然后再给出不同的结果,而又不会在超时后取消将来的结果?我有一项服务(我们称它为...

java completable-future thread-sleep vavr
1个回答
3
投票

首先需要指出CompletableFuture的工作方式(或为什么这样命名):

© www.soinside.com 2019 - 2024. All rights reserved.