简体   繁体   中英

Ways to handle error in CompletableFuture in Java

I was working with CompletableFuture and I came across the following use case:

public void someFunction () {
  CompletableFuture.runAsync(() -> computations())
  .exceptionally((err) -> {
    log.info(err + "");
    return null;
  });
}

public void computations() {
  
  list.stream.forEach(() -> otherFunction());
  
}

public void otherFunction() {
  //computations which can throw some error
}

As you can see here, I can easily log the error in exceptionally block but what if I don't want my program to halt and continue until all the elements of list is iterated. I want to log the error if there is any and proceed to the next and not halt the application.

You can try catch the code that can throw the Exception as follow: to handle the exception directly for each item without blocking the forEach loop

public void otherFunction() {
   try {
     //computations which can throw some error
   } catch (Exception e) {
     // Log and handle error without blocking analysis for following items
   }
 }

If you already know what exception can be thrown by otherFunction you can explicitly catch only that exception and leave exceptionally as a system to handle other exceptions.

If you want to cache the exact exception and handle it at a later time, you could also do something like this (note that this will only throw the last exception thrown by the list iteration):

    public void someFunction () {
        CompletableFuture.runAsync(() -> {
            try {
                computations();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public void computations() throws Exception {
        
        AtomicReference<Exception> exception = new AtomicReference<>();
        
        list.forEach(() -> {
            try {
                otherFunction();
            } catch (Exception e) {
                exception.set(e);
            }
        });
        
        if (exception.get() != null)
            throw exception.get();
    }

    public void otherFunction() throws Exception {
        throw new Exception();
    }

Or if you want to cache all exceptions:

    public void someFunction () {
        CompletableFuture.runAsync(() -> {
            List<Exception> exceptions = computations();
            exceptions.forEach(Throwable::printStackTrace);
        });
    }

    public List<Exception> computations() {
        final List<Exception> exceptions = new ArrayList<>();
        list.forEach(() -> {
            try {
                otherFunction();
            } catch (Exception e) {
                exceptions.add(e);
            }
        });
        return exceptions;
    }

    public void otherFunction() throws Exception {
        throw new Exception();
    }

In writing code with CompletableFuture, exception handling has a significant role to play. IncompleteFuture provides three methods for handling them: handle(), whenComplete(), and exceptionally(). You can easily get lost if you have no experience with APIs because they look so similar. The following section explains the differences between them and helps you determine which is the best option for your situation. My goal is to describe each API in detail, then compare their usage, and finally provide some examples where each API is most useful. Though the info is written in Java 11, the concepts should still be familiar in Java 8. Let's begin.

handle: By passing the result and the exception of the current complete future to method handle(), you can transform the current result to another result or retrieve the exception. Given "Oops" as an exception, we can use handle() to handle the result and exception, either recovering from the exception or returning the message directly.

The result is not available in the method exceptionally(). You have access to the exception instead. Since the method is named specifically for handling exceptions, this method is meant to handle only those cases. The logic in "exceptionally" will be skipped if the completable future was achieved successfully. We can make use of exceptionally to recover from failure when a failed future returns an exception "Oops".

This result will be contained in the complete future cf1:

The next example shows how a command is skipped during execution. The future returns OK when it is successful. The logic won't be executed when adding another stage for handling the exception. If the complete future is CF1, it will simply return the same value as CF0.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM