Ich habe den folgenden Code (resultierend aus my previous question), der eine Aufgabe auf einem Remote-Server plant und dann zur Fertigstellung unter Verwendung von ScheduledExecutorService#scheduleAtFixedRate
abfragt. Sobald die Aufgabe abgeschlossen ist, wird das Ergebnis heruntergeladen. Ich möchte eine Future
an den Aufrufer zurückgeben, so dass sie entscheiden können, wann und wie lange zu blockieren, und geben Sie ihnen die Option, die Aufgabe abzubrechen.ComplectableFuture # whenComplete wird nicht aufgerufen, wenn thenApply verwendet wird
Mein Problem ist, dass, wenn der Client die Future
von der download
zurückgegebene Methode storniert, whenComplete
Block nicht ausgeführt wird. Wenn ich thenApply
entferne, tut es das. Es ist offensichtlich, dass ich etwas falsch verstehe über Future
Zusammensetzung ... Was soll ich ändern?
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;
}
Auf die gleiche Note, wenn ich es tue:
return completionFuture.whenComplete(...)
statt
completionFuture.whenComplete(...);
return completionFuture;
whenComplete
führt auch nie. Dies scheint mir sehr widersprüchlich zu sein. Sollte nicht logischerweise die Future
zurückgegeben von whenComplete
die sein, an der ich mich festhalten sollte?
EDIT:
ich meinen Code geändert, um explizit die Stornorück propagieren. Es ist abscheulich und nicht lesbar, aber es funktioniert und ich konnte nicht einen besseren Weg finden:
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;
}
Es gibt nicht einmal den 'whenComplete' Block. Ich setze einen Haltepunkt und ein 'System.out.print' hinein, und weder der Haltepunkt wird getroffen, noch wird die Zeile gedruckt. Beides passiert, wenn ich das 'thenApply'-Bit lösche. – kaqqao
Nach meinem Verständnis sollte es umgekehrt sein, was Sie melden. 'completionFuture.whenComplete()' ist eine reine Funktion und sollte nichts an der Arbeitsweise von 'completionFuture' ändern. Wenn Sie das Ergebnis von 'whenComplete' nicht zurückgeben, sollte es nicht mehr erreichbar sein und GC unterliegen. –
Absolut einverstanden. Aber das ist, was ich sehe ... Wenn ich das Ergebnis von 'whenComplete' zurückgebe und es sofort abbringe, bekomme ich XXXXX nicht in der Konsole. Auf der anderen Seite, wenn ich die ursprüngliche 'completionFuture' und' cancel' _that_ zurückschicke, tue ich es. – kaqqao