javaconcurrencyjava.util.concurrent

Attach finally logic to CompletableFuture


I need to asynchronously consume future value and then run logic X. In case of any exceptions, I also want to execute logic X. In a way, it is semantically akin to the finally block.

I do not have a lot of hands-on experience with CompletableFuture. But unless I'm mistaken, I would have to resort to ugly lambdas returning nulls. I hate returning nulls as well as curly braces (they impair code readability, imo).

This code tries to load data from a DB, then use it to fill a GUI table (a JTable). In the meantime, a progress dialog is displayed. It should be disposed whenever the operation is completed, successfully or not. I changed the actual naming a little bit.

    private CompletableFuture<Void> fillTableAsync() {
        String title = "Loading table data...";
        ProgressDialog progressDialog = new ProgressDialog(title);
        CompletableFuture<Void> futureFill = CompletableFuture.supplyAsync(this::loadData)
                .thenAccept(this::fillTable)
                .thenRun(progressDialog::dispose)
                .exceptionally(t -> {
                    progressDialog.dispose();
                    return null;
                });
        SwingUtilities.invokeLater(() -> progressDialog.setVisible(true));
        return futureFill;
    }

What I want:

    .finallyRun(progressDialog::dispose);

or at least:

    .thenRun(progressDialog::dispose)
    .exceptionally(progressDialog::dispose);

While those options are not available, there may be some cleaner ways to do it that I'm not aware of.

How am I supposed to do it? Can I do it more cleanly?

Java 8. Guava. Apache (Commons, Collections).


Solution

  • Maybe you can write it like this:

       private CompletableFuture<Void> fillTableAsync() {
           String title = "Loading table data...";
           ProgressDialog progressDialog = new ProgressDialog(title);
           CompletableFuture<Void> futureFill = CompletableFuture.supplyAsync(this::loadData)
                   .thenAccept(this::fillTable)
                   .whenComplete((result, exception) -> progressDialog.dispose());
           SwingUtilities.invokeLater(() -> progressDialog.setVisible(true));
           return futureFill;
       }