DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Problems With Nested CompletableFuture in Java
  • Mastering Spring: Synchronizing @Transactional and @Async Annotations With Various Propagation Strategies
  • Demystifying Basics of Async/Await in Python
  • Proper Java Exception Handling

Trending

  • Measuring the Impact of AI on Software Engineering Productivity
  • How Large Tech Companies Architect Resilient Systems for Millions of Users
  • Artificial Intelligence, Real Consequences: Balancing Good vs Evil AI [Infographic]
  • Automatic Code Transformation With OpenRewrite
  1. DZone
  2. Coding
  3. Java
  4. Mastering Exception Handling in Java CompletableFuture: Insights and Examples

Mastering Exception Handling in Java CompletableFuture: Insights and Examples

CompletableFuture simplifies many aspects of concurrent programming, it's crucial to understand how to handle exceptions effectively.

By 
Andrei Tuchin user avatar
Andrei Tuchin
DZone Core CORE ·
Jan. 25, 24 · Analysis
Likes (15)
Comment
Save
Tweet
Share
27.5K Views

Join the DZone community and get the full member experience.

Join For Free

Java CompletableFuture is a versatile tool for writing asynchronous, non-blocking code. While CompletableFuture simplifies many aspects of concurrent programming, it's crucial to understand how to handle exceptions effectively. In this article, we'll explore the ins and outs of handling exceptions in CompletableFuture, providing insights and real-world examples.

Exception Handling Basics

Exception handling is essential in CompletableFuture-based applications to gracefully deal with unexpected errors that might occur during asynchronous tasks. CompletableFuture provides several methods to facilitate exception handling.


Java: A Complete Tutorial from Zero to JDBC Course.*

*Affiliate link. See Terms of Use.

Handling Exceptions exceptionally()

The exceptionally() method is your go-to choice for handling exceptions in CompletableFuture. It allows you to define an alternative value or perform custom logic when an exception occurs during the execution of a CompletableFuture.

Java
 
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // Simulate an exception
    throw new RuntimeException("Something went wrong");
});

CompletableFuture<Integer> handledFuture = future.exceptionally(ex -> {
    System.err.println("Exception: " + ex.getMessage());
    return -1; // Provide a default value
});

handledFuture.thenAccept(result -> System.out.println("Result: " + result));


In this example, if an exception occurs during the execution of the future, the exceptionally() method handles it by printing an error message and returning a default value of -1. The result is then printed as "Result: -1."

Handling Success and Failure handle()

The handle() method is a more comprehensive approach to exception handling. It allows you to handle both successful results and exceptions in a single callback. This method takes a BiFunction that processes the result and the exception (if one occurred).

Java
 
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // Simulate an exception
    throw new RuntimeException("Something went wrong");
});

CompletableFuture<String> handledFuture = future.handle((result, ex) -> {
    if (ex != null) {
        System.err.println("Exception: " + ex.getMessage());
        return "Default";
    } else {
        return "Result: " + result;
    }
});

handledFuture.thenAccept(result -> System.out.println(result));


In this instance, the handle() function examines whether an exception has occurred (verified through ex != null) and responds by printing an error message and furnishing a default value of "Default." In the absence of an exception, it proceeds to process the successful outcome and provides "Result: " in conjunction with the result value. The outcome comprises both "Exception: Something went wrong" and "Default." 

Exception Propagation in CompletableFuture

Understanding how exceptions propagate within a CompletableFuture chain is crucial. Exceptions can be propagated down the chain, affecting subsequent operations. Here's how it works.

Exception Propagation in thenApply() and Similar Methods: In operations like thenApply(), if an exception occurs in the source CompletableFuture, it propagates to the dependent CompletableFuture, causing the dependent operation to be skipped.

Java
 
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    throw new RuntimeException("Something went wrong");
});

CompletableFuture<String> handledFuture = future.thenApply(result -> "Result: " + result);

handledFuture.exceptionally(ex -> {
    System.err.println("Exception: " + ex.getMessage());
    return "Default";
});

// The exception propagates to handledFuture, skipping the transformation in thenApply.


Exception Handling in exceptionally() or handle(): You can intercept exceptions and handle them using exceptionally() or handle(). If you don't handle an exception in these methods, it will propagate further down the chain. 

Java
 
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    throw new RuntimeException("Something went wrong");
});

CompletableFuture<Integer> handledFuture = future
    .exceptionally(ex -> {
        System.err.println("Exception: " + ex.getMessage());
        return -1;
    })
    .thenApply(result -> result * 2);

handledFuture.thenAccept(result -> System.out.println("Result: " + result));


In this example, the exceptionally() method handles the exception, preventing it from propagating to the subsequent thenApply() operation.

Combining CompletableFuture and Exception Handling

Handling exceptions becomes more powerful when combined with CompletableFuture's ability to combine multiple asynchronous tasks. Here, we'll explore how to handle exceptions when combining CompletableFuture instances.

Combining Results thenCombine()

The thenCombine() method allows you to combine the results of two CompletableFuture instances. When an exception occurs in either of the source CompletableFutures, it can be handled using exceptionally().

Java
 
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
    throw new RuntimeException("Something went wrong in future1");
});

CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 7);

CompletableFuture<Integer> combinedFuture = future1
        .exceptionally(ex -> {
            System.err.println("Exception in future1: " + ex.getMessage());
            return -1;
        })
        .thenCombine(future2, (result1, result2) -> result1 + result2);

combinedFuture.thenAccept(result -> System.out.println("Result: " + result));


In this example, future1 throws an exception, but it's gracefully handled using exceptionally(). The result is -1 for future1, and 7 for future2, leading to a final result of 6 for combinedFuture.

Exception Handling and thenCompose()

The thenCompose() method, used for sequential chaining, also allows exception handling. It's particularly useful when combining CompletableFuture instances that might produce exceptions.

Java
 
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 5);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
    throw new RuntimeException("Something went wrong in future2");
});

CompletableFuture<Integer> combinedFuture = future1.thenCompose(result1 ->
    future2
        .exceptionally(ex -> {
            System.err.println("Exception in future2: " + ex.getMessage());
            return -1;
        })
        .thenApply(result2 -> result1 + result2));

combinedFuture.thenAccept(result -> System.out.println("Result: " + result));


In this example, future1 completes successfully, but future2 throws an exception. The exception in future2 is handled, and the result is combined with future1 to yield a final result of 4 for combinedFuture.

Timeout Handling in CompletableFuture

In real-world applications, you often need to set timeouts for asynchronous operations to prevent them from blocking indefinitely. CompletableFuture offers mechanisms for timeout handling.

Providing a Default Value completeOnTimeout()

The completeOnTimeout() method allows you to specify a default value to complete a CompletableFuture if it doesn't complete within a specified time frame.

Java
 
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // Simulate a long-running operation
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 42;
});

CompletableFuture<Integer> withTimeout = future.completeOnTimeout(0, 2, TimeUnit.SECONDS);


In this example, if future doesn't complete within 2 seconds, it will be completed with a default value of 0.

Handling Timeout Exception orTimeout()

The orTimeout() method allows you to set a maximum time limit for a CompletableFuture. If the CompletableFuture doesn't complete within the specified time, a TimeoutException is thrown, which you can handle using exceptionally().

Java
 
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // Simulate a long-running operation
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 42;
});

CompletableFuture<Integer> withTimeout = future.orTimeout(2, TimeUnit.SECONDS)
        .exceptionally(ex -> {
            System.err.println("Timeout occurred: " + ex.getMessage());
            return -1; // Handle the timeout gracefully
        });


In this example, if future takes more than 2 seconds to complete, a TimeoutException is thrown and handled by exceptionally(), providing a default value of -1.

Custom Exception Handling

Custom exception handling in CompletableFuture allows you to define specific error-handling logic based on your application's requirements. This can include logging, retrying, or taking other corrective actions.

Java
 
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // Simulate an exception
    throw new CustomException("Custom exception occurred");
});

CompletableFuture<Integer> handledFuture = future.exceptionally(ex -> {
    if (ex instanceof CustomException) {
        // Custom handling for the specific exception
        System.err.println("Custom Exception: " + ex.getMessage());
        return -1;
    } else {
        // Handle other exceptions generically
        System.err.println("Generic Exception: " + ex.getMessage());
        return -2;
    }
});

handledFuture.thenAccept(result -> System.out.println("Result: " + result));


In this example, we handle a custom exception differently from other exceptions. You can tailor your exception-handling logic to your specific use case.

Conclusion

Java CompletableFuture provides robust tools for handling exceptions in asynchronous code. By using methods like exceptionally(), handle(), and combining them with timeout mechanisms like completeOnTimeout() and orTimeout(), you can build resilient and responsive applications that gracefully handle errors.

Understanding how exceptions propagate through CompletableFuture chains and how to handle them effectively is crucial for writing reliable asynchronous code in Java. With these insights and examples, you are well-equipped to master exception handling in CompletableFuture and develop robust concurrent applications.

Error message Java (programming language) Asynchronous method invocation

Opinions expressed by DZone contributors are their own.

Related

  • Problems With Nested CompletableFuture in Java
  • Mastering Spring: Synchronizing @Transactional and @Async Annotations With Various Propagation Strategies
  • Demystifying Basics of Async/Await in Python
  • Proper Java Exception Handling

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!