CompletableFuture | thenApply против thenCompose

119

Я не могу понять разницу между thenApply() и thenCompose().

Итак, может ли кто-нибудь предоставить допустимый вариант использования?

Из документов Java:

thenApply(Function<? super T,? extends U> fn)

Возвращает новое значение, CompletionStageкоторое при нормальном завершении этого этапа выполняется с результатом этого этапа в качестве аргумента предоставленной функции.

thenCompose(Function<? super T,? extends CompletionStage<U>> fn)

Возвращает новое значение, CompletionStageкоторое, когда этот этап завершается нормально, выполняется с этим этапом в качестве аргумента предоставленной функции.

Я понимаю, что второй аргумент thenComposeрасширяет CompletionStage там, где thenApplyего нет.

Может ли кто-нибудь привести пример, в каком случае я должен использовать thenApplyи когда thenCompose?

GuyT
источник
39
Вы понимаете разницу между mapи flatMapв Stream? thenApplyэто mapи thenComposeесть flatMapиз CompletableFuture. Вы используете, thenComposeчтобы избежать CompletableFuture<CompletableFuture<..>>.
Миша
2
@Misha Спасибо за ваш комментарий. Да, я знаю разницу между mapи, flatMapи я понимаю вашу точку зрения.
Еще
Это очень хорошее руководство для начала работы с CompletableFuture - baeldung.com/java-completablefuture
thealchemist

Ответы:

168

thenApply используется, если у вас есть функция синхронного сопоставления.

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenApply(x -> x+1);

thenComposeиспользуется, если у вас есть функция асинхронного сопоставления (т. е. та, которая возвращает a CompletableFuture). Затем он вернет будущее с результатом напрямую, а не вложенное будущее.

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1));
Джо С
источник
14
Почему программисту следует использовать .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1))вместо .thenApplyAsync(x -> x+1)? Синхронизация или асинхронность не имеет значения.
Holger
17
Они бы так не поступили. Однако, если сторонняя библиотека, которую они использовали, вернула a CompletableFuture, то это был thenComposeбы способ сгладить структуру.
Joe C
1
@ArunavSanyal, голоса показывают иную картину. Ответ ясен и краток.
Alex Shesterov
@Holger прочитайте мой другой ответ, если вы запутались, thenApplyAsyncпотому что это не то, что вы думаете.
1283822
@ 1283822 Я не знаю, что заставляет вас думать, что я был сбит с толку, и в вашем ответе нет ничего, подтверждающего ваше утверждение, что «это не то, что вы думаете».
Holger
49

Я думаю, что ответ, опубликованный @Joe C, вводит в заблуждение.

Позвольте мне попытаться объяснить разницу между thenApplyи thenComposeна примере.

Предположим, у нас есть 2 метода: getUserInfo(int userId)и getUserRating(UserInfo userInfo):

public CompletableFuture<UserInfo> getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)

Оба типа возвращаемых методов - это CompletableFuture.

Мы хотим getUserInfo()сначала вызвать , а по его завершении вызвать getUserRating()с результатом UserInfo.

По завершении getUserInfo()метода давайте попробуем оба thenApplyи thenCompose. Разница в возвращаемых типах:

CompletableFuture<CompletableFuture<UserRating>> f =
    userInfo.thenApply(this::getUserRating);

CompletableFuture<UserRating> relevanceFuture =
    userInfo.thenCompose(this::getUserRating);

thenCompose()работает как Scala,flatMap который выравнивает вложенные фьючерсы.

thenApply()вернули вложенные фьючерсы такими, какие они были, но thenCompose()сгладили вложенные, CompletableFuturesчтобы было легче связать с ним больше вызовов методов.

Доржи
источник
2
Тогда ответ Джо Си не вводит в заблуждение. Это правильно и более лаконично.
koleS
2
Это было очень полезно :-)
dstibbe
1
Имхо, это плохой дизайн для написания CompletableFuture <UserInfo> getUserInfo и CompletableFuture <UserRating> getUserRating (UserInfo) \\ вместо этого должен быть UserInfo getUserInfo () и int getUserRating (UserInfo), если я хочу использовать его async и цепочку, тогда я могу используйте ompletableFuture.supplyAsync (x => getUserInfo (userId)). thenApply (userInfo => getUserRating (userInfo)) или что-то в этом роде, это более читабельно imho, и не обязательно
переносить
@ user1694306 Плохой дизайн или нет, зависит от того, содержится ли рейтинг пользователя в UserInfo(тогда да) или его нужно получать отдельно, может быть, даже дорого (тогда нет).
glglgl
42

Обновленные Javadocs в Java 9, вероятно, помогут лучше понять это:

thenApply

<U> CompletionStage<U> thenApply​(Function<? super T,? extends U> fn)

Возвращает новое значение, CompletionStageкоторое при нормальном завершении этого этапа выполняется с результатом этого этапа в качестве аргумента предоставленной функции.

Этот метод аналогичен Optional.mapи Stream.map.

Смотрите в CompletionStageдокументации правила, касающиеся исключительного завершения.

thenCompose

<U> CompletionStage<U> thenCompose​(Function<? super T,? extends CompletionStage<U>> fn)

Возвращает новый, CompletionStageкоторый завершается тем же значением, что и CompletionStageвозвращаемое данной функцией.

Когда этот этап завершается нормально, данная функция вызывается с результатом этого этапа в качестве аргумента, возвращая другой CompletionStage. Когда этот этап завершается нормально, CompletionStageвозвращаемое этим методом значение завершается с тем же значением.

Для обеспечения прогресса предоставленная функция должна обеспечить окончательное завершение своего результата.

Этот метод аналогичен Optional.flatMapи Stream.flatMap.

Смотрите в CompletionStageдокументации правила, касающиеся исключительного завершения.

Дидье Л
источник
14
Интересно , почему они не назвали эти функции mapи flatMapв первую очередь.
Маттиас Браун
1
@MatthiasBraun Я думаю, это потому, что thenApply()будет просто вызывать Function.apply(), и thenCompose()это немного похоже на составление функций.
Didier L
14

thenApplyи thenComposeявляются методами CompletableFuture. Используйте их, когда намереваетесь сделать что-то, чтобы добиться CompleteableFutureрезультата с помощью Function.

thenApplyи thenComposeоба возвращают CompletableFutureкак свой собственный результат. Вы можете объединить несколько thenApplyили thenComposeвместе. Поставьте a Functionдля каждого вызова, результат которого будет вводом для следующего Function.

Предоставленному Functionвами иногда необходимо что-то делать синхронно. Тип возврата вашего Functionдолжен быть не Futureтипом. В этом случае вам следует использовать thenApply.

CompletableFuture.completedFuture(1)
    .thenApply((x)->x+1) // adding one to the result synchronously, returns int
    .thenApply((y)->System.println(y)); // value of y is 1 + 1 = 2

В других случаях вы можете захотеть выполнить здесь асинхронную обработку Function. В этом случае вам следует использовать thenCompose. Тип возврата вашего Functionдолжен быть CompletionStage. Следующий Functionв цепочке получит результат CompletionStageкак входной, таким образом разворачивая файл CompletionStage.

// addOneAsync may be implemented by using another thread, or calling a remote method
abstract CompletableFuture<Integer> addOneAsync(int input);

CompletableFuture.completedFuture(1)
    .thenCompose((x)->addOneAsync(x)) // doing something asynchronous, returns CompletableFuture<Integer>
    .thenApply((y)->System.println(y)); // y is an Integer, the result of CompletableFuture<Integer> above

Это похоже на идею Javascript Promise. Promise.thenможет принимать функцию, которая либо возвращает значение, либо Promiseзначение. Причина, по которой эти два метода имеют разные имена в Java, связана с общим стиранием . Function<? super T,? extends U> fnи Function<? super T,? extends CompletionStage<U>> fnсчитаются одним и тем же типом среды выполнения - Function. Таким образом, thenApplyи thenComposeон должен быть четко назван, иначе компилятор Java будет жаловаться на идентичные сигнатуры методов. Конечным результатом является то, что Javascript Promise.thenреализован в двух частях - thenApplyи thenCompose- в Java.

Вы можете прочитать мой другой ответ, если вас не смущает связанная функция thenApplyAsync.

1283822
источник