Docs

Documentation versions (currently viewingVaadin 24)

Consuming Futures Flow

How to use server push with CompletableFuture.

Some background jobs may use CompletableFuture to inform the user interface of results and errors. This is covered in the Returning Futures documentation page.

When building the user interface with Vaadin Flow, you can use callbacks and register them with the CompletableFuture to update your user interface.

For example, a method for handling successful completion could look like this:

private void onJobCompleted(String result) {
    Notification.show("Job completed: " + result);
}
Note
The examples on this page only work with push enabled (see Server Push).

A method for handling errors might look like this:

private void onJobFailed(Throwable error) {
    Notification.show("Job failed: " + error.getMessage());
}

Note, that the error handler must accept a Throwable, and not an Exception when you’re working with CompletableFuture.

Successful Completion

If a CompletableFuture completes successfully, you can instruct it to perform a specific operation by calling the thenAccept() method on it. This method takes a Consumer as its input. When the CompletableFuture completes, it calls this consumer with the result.

Here is an example of a button click listener that starts a background job, and updates the user interface when it has completed successfully:

button.addClickListener(clickEvent -> {
    var ui = UI.getCurrent();
    service.startBackgroundJob()
        .thenAccept(ui.accessLater(this::onJobCompleted, null)); // (1)
});
  1. The UI.accessLater() method is explained on the Pushing UI Updates documentation page.

Exceptional Completion

If a CompletableFuture is completed with an exception, you can instruct it to perform a specific operation by calling the exceptionally() method on it. However, this method works in a different way than thenAccept().

The exceptionally() method takes a Function, instead of a Consumer as input. The exception is passed to the function as input. The function output is used as the result of the CompletableFuture that is returned by exceptionally().

Flow has no version of UI.accessLater() that works with Function. However, since you’re probably not interested in returning a result, you can create a helper function that adapts a Consumer to a Function, like this:

public static <T> Function<T, Void> consumerToFunction(Consumer<T> consumer) {
    return input -> {
        consumer.accept(input);
        return null;
    };
}

Here is an example of a button click listener that starts a background job, and uses the helper function to update the user interface if an error occurs:

button.addClickListener(clickEvent -> {
    var ui = UI.getCurrent();
    service.startBackgroundJob()
        .thenAccept(ui.accessLater(this::onJobCompleted, null))
        .exceptionally(consumerToFunction(ui.accessLater(this::onJobFailed, null)))
});