Docs

Documentation versions (currently viewingVaadin 25.4 (pre-release))

Returning Futures Flow

How to use CompletableFuture to interact with the user interface.

When using a Flow user interface, you can use a standard Java CompletableFuture to report results and errors to it, and to cancel the job. For reporting progress, however, you still need to use a callback.

Compared to callbacks, this approach is easier to implement in the application layer, but more difficult to implement in the UI layer. You should only use it if you’ve previously used CompletableFuture, and you need other features that it offers, like chaining completion stages together.

Returning a Result

As on the Callbacks page, the examples on this page are methods of a protected application service. They run the job with an injected TaskExecutor instead of using the @Async annotation. See User Triggered Jobs for why.

CompletableFuture can run a job on the TaskExecutor through its supplyAsync() method. The following example shows a background job that completes with either a string or an exception. If the job throws an exception, the CompletableFuture completes exceptionally:

Source code
Java
@PreAuthorize("hasAuthority('permission:startjob')")
public CompletableFuture<String> startBackgroundJob() {
    return CompletableFuture.supplyAsync(this::doSomethingThatTakesALongTime,
        taskExecutor);
}

To update the user interface, you have to add special completion stages that execute after the CompletableFuture completes. For more information about how to add these, see the Consuming Futures documentation page.

Cancelling

You can cancel a Java Future by calling its cancel() method. The method has a boolean parameter that indicates whether the thread should be interrupted or not. However, CompletableFuture, which implements Future, doesn’t use this parameter. It doesn’t therefore make a difference whether you pass true or false.

When you cancel a CompletableFuture, it completes with a CompletionException caused by a CancellationException. However, the job continues to run silently in the background until it’s finished. If you want to notify the job itself that it has been cancelled, and it should stop running at the next suitable moment, you’ll have to make some changes.

CompletableFuture has an isCancelled() method that you can use to query whether the job has been cancelled or not. However, the job can’t reach the future that supplyAsync() returns. Instead, you have to create the CompletableFuture yourself, execute the job with the TaskExecutor, and manage the state of the future. The principle is the same as when using callbacks or handles.

The earlier example would look like this when implemented using a CompletableFuture:

Source code
Java
@PreAuthorize("hasAuthority('permission:startjob')")
public CompletableFuture<String> startBackgroundJob() {
    var future = new CompletableFuture<String>();
    taskExecutor.execute(() -> {
        try {
            var step1Result = performStep1();

            if (future.isCancelled()) {
                return;
            }
            var step2Result = performStep2(step1Result);

            if (future.isCancelled()) {
                return;
            }
            var step3Result = performStep3(step2Result);

            if (future.isCancelled()) {
                return;
            }
            var result = performStep4(step3Result);
            future.complete(result);
        } catch (Exception ex) {
            future.completeExceptionally(ex);
        }
    });
    return future;
}

You don’t need to do anything with the future after it has been cancelled, as it has already been completed. Returning is enough.