The previous two articles have sorted out most of the features of the CompletableFuture; this article will sort out the rest of the CompletableFuture and compare it to RxJava.

3.6 Either

Either represents two CompletableFutures that are executed when Either CompletableFuture is computed.

The method name describe
acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) When any CompletableFuture completes, the action consumer is executed.
acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action) When any CompletableFuture completes, the action consumer is executed. Using ForkJoinPool
acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor) When any CompletableFuture completes, the action consumer is executed. Use the specified thread pool
        Random random = new Random();

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->{

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "from future1";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "from future2";
        });

        CompletableFuture<Void> future =  future1.acceptEither(future2,str->System.out.println("The future is "+str));

        try {
            future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }Copy the code

Result: The future is from future1 or The Future is from future2. Because of future1 and future2, the order of execution is random.

ApplyToEither is similar to acceptEither.

The method name describe
applyToEither(CompletionStage<? extends T> other, Function<? super T,U> fn) When any CompletableFuture completes, fn is executed and its return value is treated as the result of the new CompletableFuture<U> calculation.
applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn) When any CompletableFuture completes, fn is executed and its return value is treated as the result of the new CompletableFuture<U> calculation. Using ForkJoinPool
applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn, Executor executor) When any CompletableFuture completes, fn is executed and its return value is treated as the result of the new CompletableFuture<U> calculation. Use the specified thread pool
        Random random = new Random();

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->{

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "from future1";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{

            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "from future2";
        });

        CompletableFuture<String> future =  future1.applyToEither(future2,str->"The future is "+str);

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }Copy the code

The execution result is similar to the program above.

3.7 Other Methods

AllOf, anyOf are static methods for CompletableFuture.

3.7.1 allOf

The method name describe
allOf(CompletableFuture<? >… cfs) Ends when all Future objects are complete and returns a Future.

The CompletableFuture returned by the allOf() method does not combine the results of previous completableFutures. So we use Java 8 Stream to combine the results of multiple Futures.

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "tony");

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "cafei");

        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "aaron");

        CompletableFuture.allOf(future1, future2, future3)
                .thenApply(v ->
                Stream.of(future1, future2, future3)
                        .map(CompletableFuture::join)
                        .collect(Collectors.joining("")))
                .thenAccept(System.out::print);Copy the code

Execution result:

tony cafei aaronCopy the code

3.7.2 anyOf

The method name describe
anyOf(CompletableFuture<? >… cfs) Ends at the end of any Future object and returns a Future.
        Random rand = new Random();
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(rand.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "from future1";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(rand.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "from future2";
        });
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(rand.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "from future3";
        });

        CompletableFuture<Object> future =  CompletableFuture.anyOf(future1,future2,future3);

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }Copy the code

When anyOf() is used, as soon as a future is finished, it is over. So the result could be “from future1”, “from future2”, or “from future3”.

The difference between anyOf and acceptEither and applyToEither is that the latter two can be used in only two futures, while anyOf can be used in several futures.

3.8 Exception handling for CompletableFuture

If the CompletableFuture encounters an exception at run time, it can handle it by using get() and throwing an exception, but this is not the best approach. The CompletableFuture itself also provides several ways to handle exceptions.

3.8.1 exceptionally

The method name describe
exceptionally(Function fn) This exceptionally calculation is triggered only if the CompletableFuture throws an exception, calling function to evaluate the value.
        CompletableFuture.supplyAsync(() -> "hello world")
                .thenApply(s -> {
                    s = null;
                    int length = s.length();
                    return length;
                }).thenAccept(i -> System.out.println(i))
                .exceptionally(t -> {
                    System.out.println("Unexpected error:" + t);
                    return null;
                });Copy the code

Execution result:

Unexpected error:java.util.concurrent.CompletionException: java.lang.NullPointerExceptionCopy the code

A slight modification to the above code fixes null pointer exceptions.

        CompletableFuture.supplyAsync(() -> "hello world")
                .thenApply(s -> {
// s = null;
                    int length = s.length();
                    return length;
                }).thenAccept(i -> System.out.println(i))
                .exceptionally(t -> {
                    System.out.println("Unexpected error:" + t);
                    return null;
                });Copy the code

Execution result:

11Copy the code

3.8.2 whenComplete

WhenComplete was introduced in the previous article. It is much like exceptionally. It can catch exceptions at any stage. If there are no exceptions, the action is performed.

        CompletableFuture.supplyAsync(() -> "hello world")
                .thenApply(s -> {
                    s = null;
                    int length = s.length();
                    return length;
                }).thenAccept(i -> System.out.println(i))
                .whenComplete((result, throwable) -> {

                    if(throwable ! =null) {
                       System.out.println("Unexpected error:"+throwable);
                    } else{ System.out.println(result); }});Copy the code

Execution result:

Unexpected error:java.util.concurrent.CompletionException: java.lang.NullPointerExceptionCopy the code

A similar method to whenComplete is handle, the use of which was discussed in the previous article.

Iv. CompletableFuture VS Java8 Stream VS RxJava1 & RxJava2

CompletableFuture has many features similar to RxJava, so make a comparison between CompletableFuture, Java 8 Stream, and RxJava.

composable lazy resuable async cached push back pressure
CompletableFuture support Does not support support support support support Does not support
Stream support support Does not support Does not support Does not support Does not support Does not support
Observable(RxJava1) support support support support support support support
Observable(RxJava2) support support support support support support Does not support
Flowable(RxJava2) support support support support support support support

In live.

Java 8 provides a function-style asynchronous and event-driven programming model, CompletableFuture, that does not cause clogging. The fork/join framework behind CompletableFuture is used to start new threads for asynchronous and concurrent implementation. Of course, we can also do this by specifying thread pools.

CompletableFuture is a big deal, especially for microservice architectures. In a specific scenario, the product page of e-commerce may involve product details, product review services, related product recommendation services, and so on. When getting product information (/ productDetails? Productid = XXX), multiple services need to be called to process this one request and return the result. Concurrent programming may be involved here, but we can do that with CompletableFuture or RxJava in Java 8.

Java8’s new asynchronous Programming method CompletableFuture(1)