CompletableFuture#whenComplete不被调用,如果thenApply被使用 [英] CompletableFuture#whenComplete not called if thenApply is used

查看:857
本文介绍了CompletableFuture#whenComplete不被调用,如果thenApply被使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码(由我的上一个问题)在远程服务器上安排任务,然后使用 ScheduledExecutorService#scheduleAtFixedRate 轮询它完成。任务完成后,它将下载结果。
我想向调用者返回一个 Future ,以便他们可以决定阻塞的时间和长度,并给他们选择取消任务。



我的问题是,如果客户取消由下载返回的 Future c>方法, whenComplete 块不执行。如果我删除 thenApply 它。很明显我误解了未来组合...我应该改变什么?

  public Future< Object>下载(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); //永远不会发生,除非thenApply被删除
checkFuture。 cancel(true);
executor.shutdown();
});
return completionFuture;
}

同样的注意事项,如果我这样做:

  return completionFuture.whenComplete(...)

而不是

  completionFuture.whenComplete 
return completionFuture;

whenComplete 这似乎对我来说是违反直觉的。不应该逻辑上返回的未来 whenComplete 是我应该持有吗?



EDIT:



我更改了我的代码,明确地反向传播取消。这是令人憎恶和不可读的,但它的工作,我找不到一个更好的方法:

  public Future< Object&下载(Something something)throws ChartDataGenException,Exception {
String jobId = schedule(report);
CompletableFuture< String> job = pollForCompletion(jobId);
CompletableFuture< Object> result = job.thenApply(this :: download);
resulting.whenComplete((result,thrown) - > {
if(resulting.isCancelled()){//检查不是必需的,但更好地传达意图
job.cancel (true);
}
});
return result;
}


解决方案

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

所以当你取消 thenApply 未来时,原来的 completionFuture 保持不受影响,因为它不依赖于 thenApply 阶段。然而,如果你不链接 thenApply 阶段,你将返回原始的 completionFuture 实例并取消阶段导致所有依赖阶段取消,导致 whenComplete 动作立即执行。



但是当 thenApply 阶段取消, completionFuture 仍然可能会完成当 pollRemoteServer(jobId).equals (COMPLETE)条件满足,因为轮询不停止。但我们不知道 jobId = schedule(something) pollRemoteServer(jobId)的关系。如果您的应用程序状态发生变化,这种情况在取消下载之后永远无法实现,那么这个未来将永远不会完成...






<关于你最后一个问题,哪个未来是我应该持有的?,没有要求有一个线性的期货链,事实上,而 CompletableFuture 使创建这样一个链很容易,更多的时候,它是最不有用的事情,因为你可以只写一个代码块,如果你有一个线性依赖。你的链接两个独立阶段的模式是正确的,但取消不能通过它,但它不会通过线性链工作。



如果你想能够取消源阶段,你需要一个引用它,但如果你想要能够得到一个依赖阶段的结果,你还需要一个引用这个阶段。


I have the following code (resulting from my previous question) that schedules a task on a remote server, and then polls it completion using ScheduledExecutorService#scheduleAtFixedRate. Once the task is complete, it downloads the result. I want to return a Future to the caller so they can decide when and how long to block, and give them the option to cancel the task.

My problem is that if the client cancels the Future returned by the download method, whenComplete block doesn't execute. If I remove thenApply it does. It's obvious I'm misunderstanding something about Future composition... What should I change?

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;
}

On the same note, if I do:

return completionFuture.whenComplete(...)

instead of

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

whenComplete also never executes. This seems very counterintuitive to me. Shouldn't logically the Future returned by whenComplete be the one I should hold on to?

EDIT:

I changed my code to explicitly back-propagate the cancellation. It's abhorrent and unreadable, but it works and I couldn't find a better way:

public Future<Object> download(Something something) throws ChartDataGenException, Exception {
        String jobId = schedule(report);
        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;
}

解决方案

Your structure is as follows:

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

So when you cancel the thenApply future, the original completionFuture object remains unaffected as it doesn’t depend on the thenApply stage. If, however, you don’t chain the thenApply stage, you’re returning the original completionFuture instance and canceling this stage causes the cancellation of all dependent stages, causing the whenComplete action to be executed immediately.

But when the thenApply stage is cancelled, the completionFuture still may get completed when the pollRemoteServer(jobId).equals("COMPLETE") condition is fulfilled, as that polling doesn’t stop. But we don’t know the relationship of jobId = schedule(something) and pollRemoteServer(jobId). If your application state changes in a way that this condition can never be fulfilled after canceling a download, this future will never complete…


Regarding your last question, which future is "the one I should hold on to?", there is no requirement to have a linear chain of futures, in fact, while the convenience methods of CompletableFuture make it easy to create such a chain, more than often, it’s the least useful thing to do, as you could just write a block of code, if you have a linear dependency. Your model of chaining two independent stages is right, but cancellation doesn’t work through it, but it wouldn’t work through a linear chain either.

If you want to be able to cancel the source stage, you need a reference to it, but if you want to be able to get the result of a dependent stage, you’ll need a reference to that stage too.

这篇关于CompletableFuture#whenComplete不被调用,如果thenApply被使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆