Java面試題#supplyAsync 和 thenCompose 之間的區別

2024年2月6日 22点热度 0人点赞

CompletableFuture.supplyAsyncCompletableFuture.thenCompose都屬於Java CompletableFuture 中的 API ,用於異步編程,但它們在不同的上下文中使用。

CompletableFuture.supplyAsync

  • 當我們想要返回 future 的異步計算時,使用 SupplyAsync。它是異步計算鏈的起點。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // some computation
    return "Result";
});

CompletableFuture.thenCompose

thenCompose是對一個CompletableFuture返回的結果進行後續操作,返回一個新的CompletableFuture。所以當返回結果是CompletableFuture函數,並且希望在另一個異步操作之後鏈接它時,可以使用它。

  • thenCompose接受一個函數,給定結果,返回一個新的 CompletableFuture。此方法用於鏈接異步操作的兩個階段,其中第二階段取決於第一階段的結果。
  • thenCompose當您需要啟動第二個異步操作(該操作取決於前一個操作的結果)並且您不希望以嵌套 future ( CompletableFuture<CompletableFuture<T>>) 結束時,請使用。
CompletableFuture<CompletableFuture<String>> future = CompletableFuture.supplyAsync(() -> {
    // some computation leading to a result
    return CompletableFuture.supplyAsync(() -> "Inner Result");
});
CompletableFuture<String> flattenedFuture = future.thenCompose(innerFuture -> innerFuture);

以下是 thenCompose 的主要用法。

  • 當第一個操作的結果依賴於第二個操作時,鏈接依賴異步操作。
  • 返回一個新的 CompletableFuture:與 thenApply 不同,thenApply用於轉換 CompletableFuture 的結果。
  • 類似 FlatMap 的行為:用函數式編程術語來說,它會鏈接多個異步操作,避免嵌套的CompletableFuture結構。

下面舉一個例子:

  1. 我們需要將一條消息通過email發送給訂閱者。
  2. 我們需要先獲取訂閱者的email,然後發送email。
  3. 我們將首先以異步方式從用戶名獲取email,然後編寫第二個 future 來發送消息。
package org.example;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class SendEmailToSubscriber {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> finalFuture = getSubscribersEmail("vikas taank")
                .thenCompose(email -> sendNewsLetter(email));
        // Wait for the final future to complete
        System.out.println(finalFuture.get()   " to vikas taank email");
    }
    // Simulates fetching user email asynchronously
    private static CompletableFuture<String> getSubscribersEmail(String userId) {
        return CompletableFuture.supplyAsync(() -> {
            // Assume this is some IO operation to get user email
            System.out.println("Fetching email for user "   userId);
            return "[email protected]";
        });
    }
    // Simulates sending an email asynchronously
    private static CompletableFuture<String> sendNewsLetter(String email) {
        return CompletableFuture.supplyAsync(() -> {
            // Assume this is some IO operation to send email
            System.out.println("Sending News Letter to "   email);
            return "News Letter sent";
        });
    }
}

組合兩個CompletableFuture例子:

import reactor.core.publisher.Mono;
import java.util.concurrent.CompletableFuture;
public class CompletableFutureUtil {
    // Method to transform CompletableFuture<Mono<T>> into CompletableFuture<T>
    public static <T> CompletableFuture<T> fromMonoFuture(CompletableFuture<Mono<T>> completableMono) {
        return completableMono.thenCompose(Mono::toFuture);
    }
    // Method to wait for two different CompletableFuture<T> to complete
    public static <T1, T2> CompletableFuture<Void> waitForBoth(
            CompletableFuture<T1> future1,
            CompletableFuture<T2> future2) {
        return CompletableFuture.allOf(future1, future2);
    }
    // Example usage
    public static void main(String[] args) {
        CompletableFuture<Mono<DomainObject1>> completableFuture1 = CompletableFuture.completedFuture(Mono.just(new DomainObject1()));
        CompletableFuture<Mono<DomainObject2>> completableFuture2 = CompletableFuture.completedFuture(Mono.just(new DomainObject2()));
        CompletableFuture<DomainObject1> compFuture1 = fromMonoFuture(completableFuture1);
        CompletableFuture<DomainObject2> compFuture2 = fromMonoFuture(completableFuture2);
        CompletableFuture<Void> combinedFuture = waitForBoth(compFuture1, compFuture2);
        // Wait for both futures to complete (blocking call, use cautiously)
        combinedFuture.join();
    }
}