异步更新
Environment.runLater() API 提供了一种机制,可在 webforJ 应用中从后台线程安全地更新 UI。此实验性功能支持异步操作,同时确保 UI 修改的线程安全。
The webforj-handling-timers-and-async skill can schedule timers, debouncers, and async work safely on the UI thread. After installing the webforJ AI plugin, ask your assistant:
- "Refresh this dashboard every 30 seconds."
- "Add a search-as-you-type debouncer."
- "Run this CPU-heavy work in the background and update the progress bar."
了解线程模型
webforJ 强制采用严格的线程模型,所有 UI 操作都必须在 Environment 线程上执行。此限制的原因包括:
- webforJ API 的限制:底层 webforJ API 会绑定到创建会话的线程
- 组件的线程亲和性:UI 组件维护的状态并非线程安全
- 事件分发:所有 UI 事件都在单个线程上按顺序处理
这种单线程模型可以避免竞态条件,并确保所有 UI 组件的状态一致,但在集成异步的长时间运行计算任务时会带来挑战。
每个用户会话都有自己的 Environment 和对应的 Environment 线程。后台线程不属于任何会话,因此必须指定要更新的 Environment。
RunLater API
Environment.runLater() API 提供了两种用于安排 UI 更新的方法。两种方法的第一个参数都是目标 Environment:
// Schedule a task with no return value
public static PendingResult<Void> runLater(Environment target, Runnable task)
// Schedule a task that returns a value
public static <T> PendingResult<T> runLater(Environment target, Supplier<T> supplier)
两种方法都会返回一个 PendingResult,用于跟踪任务完成状态,并提供访问结果或所发生异常的方式。
runLater(Runnable) 和 runLater(Supplier) 这两个重载会根据创建当前调用线程的线程来推测目标。如果线程池由多个用户共享,那么每个任务都会在创建该池线程的用户所对应的 Environment 中运行。这些重载自 26.10 起已弃用,并计划在下一个主要版本中移除。请始终传入目标参数。
传入目标
Environment.getCurrent() 返回当前线程的 Environment。在 Environment 线程上(构造函数、事件处理程序和生命周期方法都在该线程上运行),它会返回会话的 Environment。在后台线程上,它会返回 null。
应在目标可用时,也就是后台工作开始之前读取目标,并将其传递给后台工作:
@Route
public class DataView extends Composite<Div> {
private final ExecutorService executor = Executors.newCachedThreadPool();
public DataView() {
// The constructor runs on the Environment thread
Environment env = Environment.getCurrent();
executor.submit(() -> {
String data = fetchRemoteData();
Environment.runLater(env, () -> {
dataLabel.setText(data);
loadingSpinner.setVisible(false);
});
});
}
}
由于目标作为参数传入,因此可以从任何线程调用此方法:视图创建的线程、Spring @Async 线程、公共 ForkJoinPool、计时器线程,或 HTTP 客户端的回调线程。