CompletableFuture # whenComplete wird nicht aufgerufen, wenn thenApply verwendet wird

Ich habe den folgenden Code (resultierend aus meine vorherige Frage), der eine Aufgabe auf einem Remote-Server plant und dann mit @ die Fertigstellung abfraScheduledExecutorService#scheduleAtFixedRate. Sobald die Aufgabe abgeschlossen ist, wird das Ergebnis heruntergeladen. Ich möchte ein @ zurückgebFuture an den Anrufer, damit er entscheiden kann, wann und wie lange er blockieren soll, und ihm die Option zum Abbrechen der Aufgabe geben kann.

Mein Problem ist, dass, wenn der Client das @ stornieFuture von @ zurückgegebdownload Methode,whenComplete Block wird nicht ausgeführt. Wenn ich @ entferthenApply es tut. Es ist offensichtlich, dass ich etwas über @ missversteFuture 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 der gleichen Notiz, wenn ich tue:

return completionFuture.whenComplete(...)

Anstatt vo

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

whenComplete wird auch nie ausgeführt. Dies scheint mir sehr intuitiv zu sein. Sollte nicht logisch dasFuture zurückgegeben vonwhenComplete derjenige sein, an dem ich mich festhalten sollte?

BEARBEITEN

Ich habe meinen Code geändert, um die Stornierung explizit rückgängig zu machen. Es ist abscheulich und unlesbar, aber es funktioniert und ich könnte keinen besseren Weg finden:

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

EDIT 2:

Ich habe entdeckt tascalate-concurrent, eine wunderbare Bibliothek mit einer vernünftigen Implementierung vonCompletionStage, mit Unterstützung für abhängige Versprechen (über dasDependentPromise class), mit dem Stornierungen transparent rückgängig gemacht werden können. Scheint perfekt für diesen Anwendungsfall.

Das sollte reichen:

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

Hat diesen Ansatz nicht getestet, wohlgemerkt.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage