Принцип таких методов, как supplyAsync
, заключается в создании нового экземпляра CompletableFuture
для настройки и асинхронного задания, которое в конечном итоге complete
приведет к будущему.
Вы можете сделать то же самое для вашей нетривиальной установки :
private static WebDriver currentDriver() {
…
}
private static final ExecutorService BACKEND
= new ThreadPoolExecutor(1, 1, 1, TimeUnit.MINUTES, new PriorityBlockingQueue<>());
public static <V> CompletableFuture<V> runAsync(DriverTask<V> dt) {
CompletableFuture<V> result = new CompletableFuture<>();
class Job implements Runnable, Comparable<Job>,
CompletableFuture.AsynchronousCompletionTask {
public void run() {
try {
if(!result.isDone()) result.complete(dt.call(currentDriver()));
}
catch(Throwable t) { result.completeExceptionally(t); }
}
private TaskPriority priority() { return dt.getPriority(); }
public int compareTo(Job o) { return priority().compareTo(o.priority()); }
}
BACKEND.execute(new Job());
return result;
}
Обратите внимание, что реализация CompletableFuture.AsynchronousCompletionTask
не обязательна; это просто соглашение для пометки тех Runnable
реализаций, целью которых является завершение CompletableFuture
.
Еще одно преимущество реализации logi c состоит в том, что на этом первом этапе не нужно переносить исключения в CompletionException
. Поэтому, когда вызывающий абонент связывает exceptionally
, он увидит исходное развернутое исключение. Кроме того, вызывающий join
получит CompletionException
, отражающий местоположение кода вызова join
с исходным исключением в качестве причины, с гораздо более полезной информацией.
Цель if(!result.isDone())
перед фактической попыткой завершения пропустить ее, если CompletableFuture
был отменен (или иным образом завершен) во время ожидания в очереди. Как только попытка завершения была начата, отмена не прервет ее. Это общее поведение CompletableFuture
.