java - CompletableFuture#whenComplete not called if thenApply is used -
i have following code (resulting my previous question) schedules task on remote server, , polls completion using scheduledexecutorservice#scheduleatfixedrate
. once task complete, downloads result. want return future
caller can decide when , how long block, , give them option cancel task.
my problem if client cancels future
returned download
method, whencomplete
block doesn't execute. if remove thenapply
does. it's obvious i'm misunderstanding future
composition... should 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 removed checkfuture.cancel(true); executor.shutdown(); }); return completionfuture; }
on same note, if do:
return completionfuture.whencomplete(...)
instead of
completionfuture.whencomplete(...); return completionfuture;
whencomplete
never executes. seems counterintuitive me. shouldn't logically future
returned whencomplete
1 should hold on to?
edit:
i changed code explicitly back-propagate cancellation. it's abhorrent , unreadable, works , couldn't find 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 not necessary, communicates intent better job.cancel(true); } }); return resulting; }
your structure follows:
┌──────────────────┐ │ completionfuture | └──────────────────┘ ↓ ↓ ┌──────────────┐ ┌───────────┐ │ whencomplete | │ thenapply | └──────────────┘ └───────────┘
so when cancel thenapply
future, original completionfuture
object remains unaffected doesn’t depend on thenapply
stage. if, however, don’t chain thenapply
stage, you’re returning original completionfuture
instance , canceling stage causes cancellation of dependent stages, causing whencomplete
action executed immediately.
but when thenapply
stage cancelled, completionfuture
still may completed when pollremoteserver(jobid).equals("complete")
condition fulfilled, polling doesn’t stop. don’t know relationship of jobid = schedule(something)
, pollremoteserver(jobid)
. if application state changes in way condition can never fulfilled after canceling download, future never complete…
regarding last question, future “the 1 should hold on to?”, there no requirement have linear chain of futures, in fact, while convenience methods of completablefuture
make easy create such chain, more often, it’s least useful thing do, write block of code, if have linear dependency. model of chaining 2 independent stages right, cancellation doesn’t work through it, wouldn’t work through linear chain either.
if want able cancel source stage, need reference it, if want able result of dependent stage, you’ll need reference stage too.
Comments
Post a Comment