CompletableFuture
的 thenCompose
和 thenApply
方法都是用来处理异步计算结果的,但它们在处理方式上有很重要的区别。
1.区别
-
thenApply
thenApply
用于将前一个异步计算的结果进行转换。你提供一个函数,这个函数接受前一个计算的结果作为输入,并返回一个新的值。这个方法不会创建另一个CompletableFuture
,它只是返回一个将计算结果应用函数后的新CompletableFuture
。使用
thenApply
的例子:iniCompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello"); CompletableFuture<String> greetingFuture = future.thenApply(s -> s + " World!"); // greetingFuture 完成时会得到 "Hello World!"
-
thenCompose
thenCompose
用于链接两个异步操作,当第一个操作完成时,将其结果作为参数传递给一个返回CompletableFuture
的函数。这使得你可以创建一个平坦的结果链,即只有一个级别的CompletableFuture
,而不是嵌套的CompletableFuture<CompletableFuture<T>>
。使用
thenCompose
的例子:iniCompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello"); CompletableFuture<String> greetingFuture = future.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World!")); // greetingFuture 完成时会得到 "Hello World!"
简单来说,thenApply
类似于 map
操作,用于对结果进行映射;而 thenCompose
类似于 flatMap
操作,用于链接多个异步操作,避免嵌套的 CompletableFuture
结构。
这两个方法对于处理异步操作链中的不同阶段非常有用,选择哪个方法取决于你的具体需求: 是否需要进行异步操作链接(compose),或者仅仅需要对结果进行转换(apply)。
2.案例
假设我们有一个在线书店,我们需要首先根据用户的ID异步获取用户信息,然后再根据用户信息异步获取推荐的书籍列表。
使用 thenApply(映射结果)
typescript
import java.util.concurrent.CompletableFuture;
public class BookStore {
// 模拟异步获取用户信息
public CompletableFuture<User> getUserInfo(String userId) {
return CompletableFuture.supplyAsync(() -> {
// 模拟从数据库或远程服务获取用户信息
return new User(userId, "John Doe");
});
}
// 模拟同步获取推荐书籍列表
public List<Book> getRecommendedBooks(User user) {
// 根据用户信息同步获取推荐书籍
return Arrays.asList(new Book("Book1"), new Book("Book2"));
}
public void displayRecommendedBooks(String userId) {
CompletableFuture<List<Book>> recommendedBooksFuture = getUserInfo(userId)
.thenApply(user -> getRecommendedBooks(user)); // 使用 thenApply 对结果进行映射
recommendedBooksFuture.thenAccept(books -> books.forEach(book -> System.out.println(book.getTitle())));
}
}
在这个例子中,thenApply
使用了从getUserInfo
方法返回的User
对象,并同步地调用了getRecommendedBooks
方法来得到书籍列表。由于getRecommendedBooks
方法是同步的,这里不会引入额外的异步操作。
使用 thenCompose(链接异步操作)
现在假设getRecommendedBooks
也是一个异步操作,我们需要修改我们的方法来适应这一点。
typescript
import java.util.concurrent.CompletableFuture;
public class BookStore {
// 模拟异步获取用户信息
public CompletableFuture<User> getUserInfo(String userId) {
return CompletableFuture.supplyAsync(() -> {
// 模拟从数据库或远程服务获取用户信息
return new User(userId, "John Doe");
});
}
// 现在异步获取推荐书籍列表
public CompletableFuture<List<Book>> getRecommendedBooksAsync(User user) {
return CompletableFuture.supplyAsync(() -> {
// 模拟异步获取推荐书籍
return Arrays.asList(new Book("Book1"), new Book("Book2"));
});
}
public void displayRecommendedBooks(String userId) {
CompletableFuture<List<Book>> recommendedBooksFuture = getUserInfo(userId)
.thenCompose(user -> getRecommendedBooksAsync(user)); // 使用 thenCompose 链接另一个异步操作
recommendedBooksFuture.thenAccept(books -> books.forEach(book -> System.out.println(book.getTitle())));
}
}
在这个修改后的例子中,我们使用thenCompose
来链接两个异步操作:首先异步获取用户信息,然后在用户信息获取后,我们传递它到getRecommendedBooksAsync
方法异步获取书籍列表。thenCompose
确保我们有一个平坦的、不嵌套的CompletableFuture<List<Book>>
,我们可以在其上注册一个动作来处理最终的推荐书籍列表。
通过这两个例子,你应该能够看到thenApply
和thenCompose
在实际应用中的区别:thenApply
适用于同步结果转换,而thenCompose
适用于链接多个异步操作。