Asynchronous Tasks

The UiAsyncTasks bean allows you to execute an operation in a separate thread using the security context of the current user, and then update the UI with the result of that operation.

The UiAsyncTasks bean uses CompletableFuture under the hood.

If you need to show the progress of an operation and give users the ability to abort it, use the more powerful Background Tasks mechanism.

Async Tasks With Result

To execute a task that returns a result, use the supplierConfigurer() builder and the supplyAsync() terminal method:

@Autowired
private UiAsyncTasks uiAsyncTasks;

private void loadCustomersAsync() {
    uiAsyncTasks.supplierConfigurer(this::loadCustomers) (1)
            .withResultHandler(customers -> {
                customersDc.setItems(customers); (2)
                notifications.create("Customers loaded").show();
            })
            .supplyAsync();
}

private List<Customer> loadCustomers() {
    return customerService.loadCustomers();
}
1 Supplier passed into the supplierConfigurer() method will be executed with the security context of the current user.
2 Code executed inside the withResultHandler() consumer can safely update view components.

Async Tasks Without Result

To execute a task that doesn’t return a result, use the runnableConfigurer() builder and the runAsync() terminal method:

private void synchronizeCustomersAsync() {
    uiAsyncTasks.runnableConfigurer(this::synchronizeCustomers)
            .withResultHandler(() -> {
                resultField.setValue("Synchronization completed");
            })
            .runAsync();
}

private void synchronizeCustomers() {
    customerService.synchronizeCustomers();
}

Exception Handling

By default, if the task execution fails with an exception, the exception is written to the application log. You can customize this behavior by providing an exception handler:

private void loadCustomersAndHandleException() {
    uiAsyncTasks.supplierConfigurer(this::loadCustomers)
            .withResultHandler(customers -> {
                //...
            })
            .withExceptionHandler(ex -> {
                errorField.setValue(ex.getMessage());
            })
            .supplyAsync();
}

Timeout Handling

Use the withTimeout() method to set the execution timeout value. If the timeout is exceeded, the TimeoutException will be thrown. The TimeoutException can be processed in the withExceptionHandler() method.

private void loadCustomersWithTimeout() {
    uiAsyncTasks.supplierConfigurer(this::loadCustomers)
            .withResultHandler(customers -> {
                //...
            })
            .withTimeout(20, TimeUnit.SECONDS)
            .withExceptionHandler(ex -> {
                String errorText;
                if (ex instanceof TimeoutException) {
                    errorText = "Timeout exceeded";
                } else {
                    errorText = ex.getMessage();
                }
                errorField.setValue(errorText);
            })
            .supplyAsync();
}

If no explicit timeout values is specified, the default value of 5 minutes will be used. To change this default, use the jmix.ui.async-task.default-timeout-sec application property.

ExecutorService Configuration

The UiAsyncTasks bean uses its own ExecutorService to run tasks in separate threads. To modify the default pool size of the ExecutorService, use the jmix.ui.async-task.executor-service.maximum-pool-size application property.