5

ScheduledExecutorService#scheduleAtFixedRateを使用して、リモートサーバー上でタスクをスケジュールし、完了をポーリングする次のコードがあります(my previous questionの結果)。タスクが完了すると、結果がダウンロードされます。 呼び出し元にFutureを返信して、ブロックする時期と時間を決定し、タスクをキャンセルするオプションを与えることができます。CompletableFuture#thenApplyが使用されている場合は、whenCompleteは呼び出されません。

クライアントがを返した場合、downloadメソッドで返された場合、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; 
} 

、私がしなければ:代わりに

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

whenComplete

return completionFuture.whenComplete(...) 

も実行されません。これは私にとって非常に直感的ではないようです。論理的には、がwhenCompleteで返されてはいけません。

EDIT:

は私が明示的にキャンセルをバック伝播するために自分のコードを変更しました。それは忌まわしいと読めないのですが、それは動作し、私はより良い方法を見つけることができませんでした:次のように

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

'whenComplete'ブロックに入ることさえありません。私はブレークポイントと 'System.out.print'を内部に入れ、ブレークポイントにもヒットしたり、行を印刷したりしません。両方とも 'thenApply'ビットを削除すると起こります。 – kaqqao

+0

私の理解によると、それはあなたが報告したものの逆でなければなりません。 'completionFuture.whenComplete()'は純関数であり、 'completionFuture'自体が動作する方法で何も変更すべきではありません。 'whenComplete'の結果を返さないと、到達不能になり、GCの対象となります。 –

+0

絶対に同意します。しかし、これは私が見ているものです... 'whenComplete'の結果を返し、直ちに' cancel'すると、私はコンソールにXXXXXを取得しません。一方、元の 'completionFuture'と' cancel'_that_を返すとします。 – kaqqao

答えて

4

あなたの構造は次のとおりです。

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

ですからthenApply将来、元completionFutureオブジェクトをキャンセルする場合thenApplyステージに依存しないため、影響を受けません。ただし、thenApplyステージをチェーンしていない場合は、元のcompletionFutureインスタンスを戻しています。このステージをキャンセルすると、すべての従属ステージがキャンセルされ、whenCompleteアクションがすぐに実行されます。

しかし、thenApplyステージがキャンセルされた場合、pollRemoteServer(jobId).equals("COMPLETE")の条件が満たされると、ポーリングが停止しないため、completionFutureがまだ完了することがあります。しかし、我々はjobId = schedule(something)pollRemoteServer(jobId)の関係を知らない。この条件は、ダウンロードをキャンセルした後成就することはできませんという方法でアプリケーションの状態が変化した場合、この未来は、完全な...


将来は「私が握るべきものである、あなたの最後の質問、については決してありませんか?実際には、CompletableFutureという便利な方法は、このようなチェーンを作成しやすくしますが、多くの場合、最も有用ではありません。あなたが線形の依存関係を持っているならば、コードブロック。 2つの独立したステージを連鎖させるあなたのモデルは正しいですが、取り消しはそれでは機能しませんが、リニアチェインでも機能しません。

ソースステージをキャンセルできるようにするには参照が必要ですが、従属ステージの結果を取得するには、そのステージへの参照も必要です。

+0

OPは 'whenComplete'の結果を必要としません。しかし、私の理解は、この段階を明示的に保持しなければならないことです。それ以外の場合は、到達不能であり、取り消し時に存在しない場合もあります。 –

+0

@MarkoTopolnik私は、あなたが 'whenComplete'を連鎖したものへの参照を保持するという、元の未来を推測します。そうでなければ、それは真剣にうんざりです。 – kaqqao

+0

@kaqqaoこれはおそらく、これが実装されることを期待する方法のために正しいでしょうが、依然として不特定の動作であり、依然として頼りないものです。 –

関連する問題