> Markdown version of [UI Interaction](https://vaadin.com/docs/next/building-apps/business-logic/background-jobs/interaction). Section index: [llms.txt](https://vaadin.com/docs/next/building-apps/llms.txt)

# User Interface Interaction

Some background jobs execute business processes in the background. The end user may see the result of the job, but doesn’t have to interact directly with it. Scheduled and event triggered jobs are typically in this category.

Then there are jobs that need to interact with the user interface. For instance, a job may want to update a progress indicator while running, and notify the user when it’s finished or an error has occurred. Furthermore, the user may want to cancel a running job before it has completed. This page explains different options for allowing a user to interact with a background job, and vice versa.

## <a id="why-use-a-background-job"></a>Why Use a Background Job?

In a Flow application, Vaadin holds the lock of the user’s session while it handles a request from the browser. If a listener calls a slow operation directly, Vaadin can’t send the response until the operation has finished. Until then, the user interface doesn’t respond to the user — in any browser tab that belongs to the same session.

To keep the user interface responsive, run the slow operation as a background job, and let the job report back to the user interface.

## <a id="complete-example"></a>Complete Example

The following Flow example shows all the parts together. An application service runs a job in a background thread. A view starts the job, shows its progress, and lets the user cancel it. The service uses [callbacks](https://vaadin.com/docs/next/building-apps/business-logic/background-jobs/interaction/callbacks.md) to report back to the view, and the view stores the state of the job in [signals](https://vaadin.com/docs/next/building-apps/server-push/updates.md#pushing-state-with-signals).

The example only works with server push enabled. See [Enabling Push](https://vaadin.com/docs/next/building-apps/server-push.md#enabling-push) for how to enable it.

ReportService.java

`ReportService.java`

```java
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import org.springframework.core.task.TaskExecutor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
@PreAuthorize("isAuthenticated()") // (1)
public class ReportService {

    private static final int PARTS = 10;

    private final TaskExecutor taskExecutor;

    ReportService(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    public CancellableJob generateReport(Consumer<Double> onProgress,
            Consumer<String> onComplete, Consumer<Exception> onError) {
        var cancelled = new AtomicBoolean(false);
        taskExecutor.execute(() -> { // (2)
            try {
                for (int part = 1; part <= PARTS; part++) {
                    generatePart(part);
                    if (cancelled.get()) { // (3)
                        return;
                    }
                    onProgress.accept((double) part / PARTS);
                }
                onComplete.accept("The report is ready.");
            } catch (Exception ex) {
                onError.accept(ex);
            }
        });
        return () -> cancelled.set(true); // (4)
    }

    private void generatePart(int part) throws InterruptedException {
        Thread.sleep(500); // Simulates slow work
    }

    @FunctionalInterface
    public interface CancellableJob {
        void cancel();
    }
}
```

1. Protects the service with method security, as you should with all [application services](https://vaadin.com/docs/next/building-apps/business-logic/add-service.md).

2. Runs the job in a background thread. The service uses the `TaskExecutor` instead of the `@Async` annotation, so that the security check runs in the thread of the user. See [User Triggered Jobs](https://vaadin.com/docs/next/building-apps/business-logic/background-jobs/triggers.md#user-triggered-jobs) for details.

3. Stops at the next suitable moment if the job has been cancelled.

4. Returns a handle that the view can use to cancel the job.

ReportView\.java

`ReportView.java`

```java
import jakarta.annotation.security.PermitAll;

import org.jspecify.annotations.Nullable;

import com.vaadin.demo.buildingapps.backgroundjobs.ReportService.CancellableJob;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.progressbar.ProgressBar;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.signals.local.ValueSignal;

@Route("building-apps/background-jobs/report")
@PermitAll // (1)
public class ReportView extends VerticalLayout {

    private final ReportService reportService;
    private final ValueSignal<Boolean> running = new ValueSignal<>(false); // (2)
    private final ValueSignal<Double> progress = new ValueSignal<>(0.0);
    private final ValueSignal<String> status = new ValueSignal<>("");
    private @Nullable CancellableJob job;

    public ReportView(ReportService reportService) {
        this.reportService = reportService;

        var startButton = new Button("Generate Report", event -> startJob());
        startButton.bindEnabled(running.map(isRunning -> !isRunning)); // (3)

        var progressBar = new ProgressBar();
        progressBar.bindValue(progress);
        progressBar.bindVisible(running);

        var cancelButton = new Button("Cancel", event -> {
            cancelJob();
            status.set("The report was cancelled.");
        });
        cancelButton.bindVisible(running);

        var statusText = new Span();
        statusText.bindText(status);

        add(startButton, progressBar, cancelButton, statusText);
        addDetachListener(event -> cancelJob()); // (4)
    }

    private void startJob() {
        running.set(true);
        progress.set(0.0);
        status.set("Generating the report...");
        job = reportService.generateReport(progress::set, this::onCompleted,
                this::onFailed); // (5)
    }

    private void onCompleted(String result) { // (6)
        running.set(false);
        status.set(result);
    }

    private void onFailed(Exception error) {
        running.set(false);
        status.set("Generating the report failed.");
    }

    private void cancelJob() {
        if (job != null) {
            job.cancel();
        }
        running.set(false);
    }
}
```

1. Allows any authenticated user to open the view, which matches the access rule of the service. See [Protect Views](https://vaadin.com/docs/next/building-apps/security/protect-views.md) for details.

2. Signals hold the state of the job. The job updates them from its background thread, and the bound components update automatically.

3. Derives a signal that disables the start button while the job is running.

4. Cancels the job if the user leaves the view.

5. Passes callbacks that update the signals, and stores the handle for cancelling the job.

6. Runs in the background thread. This is safe, because signals are thread-safe.

If the job can’t report its progress, call `progressBar.setIndeterminate(true)` instead of binding the value of the progress bar. The progress bar then shows that the job is running, without showing how far it has come.

## <a id="options"></a>Options

The example above is one way of letting the user interface and a background job interact. Callbacks and futures work only with Flow views, whereas reactive streams also work with [React views](https://vaadin.com/docs/next/building-apps/react.md). The following pages cover the different options in more detail:

- [Callbacks](https://vaadin.com/docs/next/building-apps/business-logic/background-jobs/interaction/callbacks.md): How to use callbacks to interact with the user interface.
- [Futures](https://vaadin.com/docs/next/building-apps/business-logic/background-jobs/interaction/futures.md): How to use CompletableFuture to interact with the user interface.
- [Producing Reactive Streams](https://vaadin.com/docs/next/building-apps/business-logic/background-jobs/interaction/reactive.md): How to use reactive streams to interact with the user interface.
