当前位置: 首页 > 面试题库 >

如果使用thenApply,则不会调用CompletableFuture#whenComplete

谢夜洛
2023-03-14
问题内容

我有以下代码,该代码在远程服务器上安排任务,然后使用轮询完成ScheduledExecutorService#scheduleAtFixedRate。任务完成后,它将下载结果。我想将a返回Future给调用方,以便他们可以确定阻止时间和阻止时间,并为他们提供取消任务的选项。

我的问题是,如果客户端取消Futuredownload方法返回的值,则whenComplete块不会执行。如果我删除thenApply它。很明显,我对Future构图有些误解…我应该改变什么?

public Future<Object> download(Something something) {
    String jobId = schedule(something);
    CompletableFuture<String> job = pollForCompletion(jobId);
    return job.thenApply(this::downloadResult);
}

private CompletableFuture<String> pollForCompletion(String jobId) {
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    CompletableFuture<String> completionFuture = new CompletableFuture<>();

    ScheduledFuture<?> checkFuture = executor.scheduleAtFixedRate(() -> {           
            if (pollRemoteServer(jobId).equals("COMPLETE")) {
                completionFuture.complete(jobId);
            }
    }, 0, 10, TimeUnit.SECONDS);
    completionFuture                
            .whenComplete((result, thrown) -> {
                System.out.println("XXXXXXXXXXX"); //Never happens unless thenApply is removed
                checkFuture.cancel(true);
                executor.shutdown();
            });
    return completionFuture;
}

同样,如果我这样做:

return completionFuture.whenComplete(...)

代替

completionFuture.whenComplete(...);
return completionFuture;

whenComplete也永远不会执行。这对我来说似乎很违反直觉。从逻辑上说,Future返回应该不是whenComplete我应该坚持的那个吗?

编辑:

我更改了代码以明确向后传播取消。这是令人讨厌和难以理解的,但是它可以正常工作,我找不到更好的方法:

public Future<Object> download(Something something) throws ChartDataGenException, Exception {
        String jobId = schedule(something);
        CompletableFuture<String> job = pollForCompletion(jobId);
        CompletableFuture<Object> resulting = job.thenApply(this::download);
        resulting.whenComplete((result, thrown) -> {
            if (resulting.isCancelled()) { //the check is not necessary, but communicates the intent better
                job.cancel(true);
            }
        });
        return resulting;
}

编辑2:

我发现了tascalate-concurrent,这是一个出色的库,提供sane的明智实现CompletionStage,并支持可以通过DependentPromise透明方式反向传播取消的相关承诺(通过类)。似乎非常适合此用例。

这应该足够了:

DependentPromise
          .from(pollForCompletion(jobId))
          .thenApply(this::download, true); //true means the cancellation should back-propagate

请注意,没有测试这种方法。


问题答案:

您的结构如下:

           ┌──────────────────┐
           │ completionFuture |
           └──────────────────┘
             ↓              ↓
  ┌──────────────┐      ┌───────────┐
  │ whenComplete |      │ thenApply |
  └──────────────┘      └───────────┘

因此,当您取消thenApply未来时,原始completionFuture对象不会受到影响,因为它与thenApply舞台无关。但是,如果您不链接该thenApply阶段,那么您将返回原始completionFuture实例,并且取消该阶段会导致所有相关阶段的取消,从而使whenComplete操作立即执行。

schedule(something)pollRemoteServer(jobId)`。如果您的应用程序状态以某种方式更改,使得取消下载后再也无法满足此条件,那么这种未来将永远无法完成…

关于您的最后一个问题,哪个期货是“我应该坚持的那个期货?”,实际上,并不需要线性的期货链,而方便的方法CompletableFuture可以轻松地创建这样的链,而不仅仅是通常,这是最没有用的事情,因为如果您具有线性依赖性,则只需编写一段代码即可。您将两个独立阶段链接在一起的模型是正确的,但是取消不能通过它起作用,但是它也不可以通过线性链来起作用。

如果希望能够取消源阶段,则需要对其进行引用,但是,如果想要获得从属阶段的结果,则也需要对该阶段进行引用。



 类似资料:
  • 问题内容: 我无法理解)和之间的区别。 那么,有人可以提供有效的用例吗? 从Java文档中: 返回一个新值,当此阶段正常完成时,将使用该阶段的结果作为所提供函数的参数来执行该操作。 返回一个新值,当此阶段正常完成时,将以该阶段作为所提供函数的参数来执行此操作。 我得到扩展的CompletionStage 的第二个参数,而没有扩展。 有人可以提供一个例子,说明我必须在什么情况下使用以及何时使用? 问

  • 我搞不清)和之间的区别。 那么,有人能提供一个有效的用例吗? 从Java文档中: 返回一个新的,当此阶段正常完成时,将以此阶段的结果作为所提供函数的参数执行该。 返回一个新的,当此阶段正常完成时,将以此阶段作为所提供函数的参数执行该。 我发现的第二个参数扩展了CompletionStage,而没有扩展CompletionStage。 谁能提供一个例子,在什么情况下我必须使用以及什么时候使用?

  • 我正在使用Jersey 2.5.1和Jackson 2.2。用于构建JSON Rest web服务。我也用2个ExceptionMappers启动并运行了它,但对于“非json”请求,应用程序不会抛出任何异常! > 如果抛出NullPointerException,将调用ExceptionMapper 如果JSon映射有问题,将调用ExceptionMapper 我的问题:1.请求正文:{}工作2

  • 我确信这是一个非常简单的东西,但由于我是一个Python/tKinter n00b,它打败了我,我已经完成了谷歌搜索。感激地得到的任何帮助:)

  • 假设我有一种情况,我 ;调用两个存储库来获取项目。 当这两个都完成并反馈结果时,我怎样才能得到反馈呢?传统上,我会一个一个地调用这两个,然后再做剩下的逻辑。在这里,我想加快这个过程。 I ;试图做thenCombine()这样的事情,但两者都是不同的对象,我无法在这个lambda中写入逻辑。有谁能提出更好的方法吗?

  • 问题内容: 假设我有以下代码: 案件: 这里输出将2.现在的情况: 我在这个博客中读到,每个步骤都是在单独的线程中执行的,并且“同时”执行(这意味着在先于结束之前先开始执行),如果是这样,那么如果第一步没有完成,那么第二步的输入参数值是多少? 如果第二步不采取措施,第一步的结果将流向何方?第三步将采取哪一步的结果? 如果第二步必须等待第一步的结果,那么意义何在? 这里x-> x + 1只是为了说明