CompletableFuture¶
CompletableFuture используется для построения пайплайна (сколь угодно ветвящегося) при обработке ассинхронных событий, во избежание callback hell.
CompletableFuture наследуется от CompletionStage и Future, но в отличии от Future - CompletableFuture может быть явно закомпличена.
CompletableFuture - фреймворк для обработки асинхронных событий, объекты которого используются часто и во внутренних библиотеках java, как возвращаемое значения выполнеения какого нибудь ассинхронного действия.
- T
get(...) См. Future
- T
join() Так же как и
getожидает завершения и возвращает значение, но в отличие отgetкидает unchecked exceptions (CompletionException - if this future completed exceptionally or a completion computation threw an exception).- T
getNow(T valueIfAbsent) Если задача еше не была выполнена - возвращается valueIfAbsent.
Добавлено в Java 8
Способы создания CompletableFuture¶
newСоздает незакомпличенное CompletableFuture.
Что бы закомплитить явно, нужно вызвать один из методов:
- boolean
cancel If not already completed, completes this CompletableFuture with a CancellationException.
- boolean
complete(T value) If not already completed, sets the value returned by
getand related methods (join…) to the given value.- boolean
completeExceptionally(Throwable ex) If not already completed, causes invocations of
getand related methods (join…) to throw the given exception.(См. Обработка ошибок)
- CompletableFuture<T>
completeAsync(Supplier<out T> supplier[, Executor executor]) Completes this CompletableFuture with the result of the given Supplier function invoked from an asynchronous task.
Returns this CompletableFuture.
- CompletableFuture<T>
completeOnTimeout(T value, long timeout, TimeUnit unit) Completes this CompletableFuture with the given value if not otherwise completed before the given timeout.
Returns this CompletableFuture.
- boolean
- static
completedFuture(U value) Возвращает новое CompletableFuture<U>, которое уже закомпличено заданным значением.
- static
supplyAsync(Supplier<U> supplier[, Executor executor]) Returns a new CompletableFuture<U> которая комплитится, как только выполнится переданное действие (асинхронно).
- static
runAsync(Runnable runnable[, Executor executor]) Returns a new CompletableFuture<Void> которая комплитится, как только выполнится переданное действие (асинхронно).
Note
Сейчас и далее для всех асинхронных операций (*Async - напр thenApplyAsync): если Executor не указан - выбирается для исполнения commonPool (а там все потоки демоны).
Объединение и пересечение результатов¶
- static CompletableFuture<Void>
allOf(CompletableFuture<?>... cfs) Возвращает новое CompletableFuture, которое комплитится после завершения всех заданных CompletableFutures.
- static CompletableFuture<Object>
anyOf(CompletableFuture<?>... cfs) Возвращает новое CompletableFuture, которое комплитится, когда завершается хоть одно из заданных CompletableFutures, с тем же результатом.
CompletionStage¶
CompletionStage - Этап асинхронного (возможно) вычисления, выполняющееся, когда завершается другой CompletionStage.
Async функции (*Async - напр thenApplyAsync) выполняются в другом (указанном или commonPool) executor’е, нежели в том потоке, где происходило предыдущее действие.
В противоположность не async-функциям, которые выполняются в том же потоке, где действие происходит. Но если действие уже было завершено - выполнение происходит в текущем потоке (в том откуда вызвана функция на CompletionStage).
На один и тот-же CompletionStage можно прицепить несколько обработчиков, при этом если они не async - они будут выпоняться последовательно в последнем потоке обработки (если действие описанное в обработчике не завершилось) или в текущем потоке (если предыдущее действие завершилось). После выполнения всех действий - проверяется, нет ли еще обработчиков, которые нужно в данном потоке прогнать, и если их нет - работа в этом потоке завершается. Поэтому если выполнение завершено и добавляется еще действие в пайплайне - то это действие уже будет происходить в текущем потоке.
Tip
Стоит помнить, что при модификациях разделяемой памяти из разных витков CompletionStage - модифицируется все равно один и тот же объект:
Note
Для всех ниже перечисленных методов на CompletionStage доступны парные async-методы (кроме exceptionally) с опциональным параметром (Executor executor),
выполняемые асинхронно, либо в указаном Executor’е, либо в FJP.commonPool .
Подписка¶
thenRun(Runnable action)Выполняет специфицированное действие, при завершении выполнения предыдущего в пайплайне. Возвращает CompletionStage<Void>.
thenAccept(Consumer<? super T> action)Выполняет специфицированное действие принимая значение полученное из предыдущего в пайплайне. Возвращает CompletionStage<Void>.
Трансформация (map)¶
thenApply(Function<? super T,? extends U> fn)Преобразует оборачиваемое значение внутри CompletionStage.
Комбинация (reduce)¶
thenCombine(CompletionStage<out U> other, BiFunction<in T,in U,out V> fn)Ожидает выполнения двух этапов (CompletionStage<T> и CompletionStage<U>) и склеивает их функцией
fn.
Композиция (flatMap)¶
thenCompose(Function<in T,out CompletionStage<U>> fn)Преобразует один CompletionStage в зависимый от него другой CompletionStage (возвращает новый CompletionStage, который завершается тем же значением, что и CompletionStage, возвращаемый данной функцией).
Обработка ошибок¶
join запихнет ошибку в CompletionException, а get в ExecutionException.
Note
Ошибка пробрасывается по цепочкам, т.е. обработчики ожидающие значение, а принимающие ошибку - не будут выполняться, а лишь пробросят ошибку далее.
exceptionally(Function<Throwable,out T> fn)Функция
fnприменяется, если на одном из предыдущих обработчиков в пайплайне было брошено исключение. При этом от момента исключения до метода exceptionally, обработчики не будут выполняться (ошибка пробрасывается по цепочкам), и наоборот exceptionally не будет выполняться, если не было получено исключение.handle(BiFunction<? super T,Throwable,? extends U> fn)Так же как exceptionally принимает исключение, но наряду с ним так же принимает предыдущее значение из пайплайна. Не трудно догадаться что либо исключение, либо значение от пред. обработчика будет == null.