最近、自分専用のライブラリーを書いていて、 CompletableFuture#thenApply
および CompletableFuture#thenApplyAsync
などを複数回呼び出せるのかがわかっていなかったし、 Javadoc にも書いていないようなので実験してみることにしました。
書いたのは次のようなコードです。
@Test void handlers2() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final CompletableFuture<String> future = CompletableFuture.supplyAsync( () -> { sleep(); // ランダムで結果を返す // 成功の場合は foo を返す // 失敗の場合はRuntimeExceptionが発生 return Result.random().apply("foo"); }, executor); // CompletableFuture#thenApplyAsync を2回呼び出す final CompletableFuture<String> f1 = future.thenApplyAsync(str -> String.format("result -> %s", str)); final CompletableFuture<String> f2 = future.thenApplyAsync( str -> { throw new RuntimeException(str); }); // 結果を文字列で表示するためのハンドラー final CompletableFuture<String> hf1 = f1.whenCompleteAsync( (str, th) -> handle(str, th) .onSuccess(s -> System.out.println(String.format("handler 1 -> %s", s))) .onError( e -> System.out.println( String.format( "handler 1 error -> %s", e.getClass().getSimpleName()))), executor); final CompletableFuture<String> hf2 = f2.whenCompleteAsync( (str, th) -> handle(str, th) .onSuccess(s -> System.out.println(String.format("handler 2 -> %s", s))) .onError( e -> System.out.println( String.format( "handler 2 error -> %s", e.getClass().getSimpleName()))), executor); CompletableFuture.allOf(hf1, hf2).whenComplete((s, t) -> latch.countDown()); latch.await(); }
結果は次のようになりました
handler 1 -> result -> foo handler 2 error -> CompletionException
というわけで、同じ CompletableFuture
のインスタンスから複数の CompletableFuture
を派生させることが可能とわかった
すると次に気になるのは、 CompletableFuture
の結果が返ってきてから、 thenApplyAsync
を呼び出せるのかという問題
先ほどのメソッドを次のように書き足してみた
@Test void handlers2() throws InterruptedException { // CountDownLatch を追加 final CountDownLatch finalLatch = new CountDownLatch(1); // ... 先ほどのメソッド latch.await(); // 先ほどのメソッドの最後の行 final CompletableFuture<String> f3 = future.thenApplyAsync(str -> String.format("after completed: %s", str)); final CompletableFuture<String> hf3 = f3.whenCompleteAsync( (str, th) -> handle(str, th) .onSuccess(s -> System.out.println(String.format("handler 3 -> %s", s))) .onError( e -> System.out.println( String.format( "handler 3 error -> %s", e.getClass().getSimpleName()))), executor); hf3.whenCompleteAsync((s,t) -> finalLatch.countDown()); finalLatch.await(); }
実行結果はこちら
handler 2 error -> CompletionException handler 1 -> result -> foo - foo handler 3 -> after completed: foo - foo
で、 complete した状態の CompletableFuture
に対して、関数を適用することも可能だということがわかった。