我正在尝试调用 vaadin 框架中运行时间较长的一些代码,这些代码将使用推送更新屏幕,但是如果该过程花费的时间太长,我希望能够取消它。
考虑到这一点,我尝试使用 Guava 的 SimpleTimeLimiter
类,但无论我做什么,我似乎都无法阻止 Vaadin 进程停止。我尝试将 SimpleTimeLimiter
放在 UI.getCurrent().access()
方法内部和外部,但它们都只是继续执行该过程,即使 SimpleTimeLimiter
抛出 TimeoutException
。但是,如果我在普通线程中使用相同的代码,它似乎可以工作......
public static void limitExecutionTime(Consumer<UI> lambda)
{
UI currentUI = UI.getCurrent();
UI.getCurrent().access(() ->
{
try
{
SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()).callWithTimeout(new Callable<Void>()
{
@Override
public Void call()
{
// This is needed to deal how Vaadin 8 handles UI's
UI.setCurrent(currentUI);
lambda.accept();
return null;
}
}, 1, TimeUnit.SECONDS);
} catch (TimeoutException | InterruptedException | ExecutionException e) {
NotificationUtils.showError("Execution took beyond the maximum allowed time.");
currentUI.push();
}
});
}
在上面的代码中,如果该方法花费的时间超过 1 秒,它将抛出 TimeoutException
并显示通知窗口。然而它将继续执行 lambda。
因此,我尝试做相反的事情,并将 UI.getCurrent().access()
放入 public Void call()
方法中,但这有完全相同的结果...
最佳答案
您应该在后台任务准备好使用一些数据更新它之后调用 UI.access
。您使用 access
方法在用户正在查看的页面上进行更改。
后台任务执行
在您的示例中,您缺少将任务取消消息传递给 call
方法的方法。为了准备从外部事件取消任务(例如单击取消按钮),您需要在任务内部考虑到这一点。以下示例展示了如何使用 Future.cancel
提供取消方法。
private void onCancelClick(Button.ClickEvent clickEvent) {
// This method is called from Vaadin UI thread. We will signal
// background task thread to stop.
futureResult.cancel(true);
}
在实际任务中,可以通过以下方式处理
private void simulateLongAndSlowCalculation() {
while (moreWorkTodo) {
if (Thread.currentThread().isInterrupted()) {
return;
}
try {
doSomeBlockingCallThatCanBeInterrupted();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
启动任务和 UI.access
启动任务时, View 应该创建任务并将其提交给执行器服务。
private void onButtonClick(Button.ClickEvent clickEvent) {
// This runTask creates important link to current UI and the background task.
// "this" object in these onTask methods is the UI object that we want
// to update. We need to have someway to pass UI object to background
// thread. UI.getCurrent() could be a parameter that is passed to the
// task as well.
Future<String> futureResult = taskService.runTask(
this::onTaskDone,
this::onTaskCancel,
this::onTaskProgress);
progressDialog = new ProgressDialog(futureResult);
progressDialog.show();
}
现在只有当我们想要更新 UI 时才需要 UI.access 方法。在此示例中,可能会在以下情况下发生
- 任务成功完成
- 任务进度已更新
- 任务被取消
请注意,以下所有方法 this
均引用启动任务的 UI 对象。因此,我们正在使用结果更新正确的 UI,而不是其他用户的 UI。
您不需要在代码中调用 UI.setCurrent。
private void onTaskProgress(double progress) {
logger.info("onTaskProgress: {}", progress);
access(() -> progressDialog.setProgress(progress));
}
private void onTaskCancel() {
logger.info("onTaskCancel");
access(() -> {
progressDialog.close();
setResult("Cancelled");
});
}
private void onTaskDone(String result) {
logger.info("onTaskDone");
access(() -> {
progressDialog.close();
setResult(result);
});
}
示例项目
我将另一个项目推送到 github,该项目展示了如何通过取消按钮取消后台任务:
https://github.com/m1kah/vaadin-background-task
编辑:添加了有关后台任务和 UI.access 的部分。更新了示例项目链接到另一个示例。
关于java - 如何将 SimpleTimeLimiter (Guava) 与 Vaadin 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47492364/