Java 中的 Promise 模式:简化异步任务以提升性能
也称为
- Deferred
- Future
Promise 设计模式的意图
Promise 设计模式用于处理异步操作,它提供了一个占位符来表示一个最初未知的结果,该结果将在将来解析。
Promise 模式的详细解释以及实际示例
实际示例
在在线披萨订购系统中,当顾客下单时,系统会立即确认订单并提供一个追踪号码(即 Promise)。披萨的制作和配送过程在后台异步进行。顾客可以使用追踪号码随时查看订单状态。一旦披萨制作完毕并开始配送,顾客就会收到关于配送状态的通知(Promise 解析)。如果出现任何问题,例如配料不足或配送延迟,顾客会收到错误通知(Promise 拒绝)。
这个比喻说明了 Promise 设计模式如何管理异步任务,将初始请求与最终结果解耦,并有效地处理结果和错误。
通俗来说
Promise 是一个正在进行的异步操作的占位符。
维基百科说
在计算机科学中,Future、Promise、Delay 和 Deferred 指的是一些并发编程语言中用于同步程序执行的结构。它们描述了一个对象,该对象充当一个代理,用于表示一个最初未知的结果,通常是因为它的值计算尚未完成。
Java 中 Promise 模式的编程示例
Promise 设计模式是一种软件设计模式,通常在并发编程中用于处理异步操作。它代表一个代理,用于表示在创建 Promise 时不一定已知的价值。它允许你将处理程序与异步操作最终的成功值或失败原因相关联。
在提供的示例中,Promise 用于异步下载文件并执行诸如行计数和字符频率分析之类的操作,展示了该模式在实际应用中的效用。
@Slf4j
public class App {
private static final String DEFAULT_URL =
"https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/promise/README.md";
private final ExecutorService executor;
private App() {
// Create a thread pool with 2 threads
executor = Executors.newFixedThreadPool(2);
}
public static void main(String[] args) {
var app = new App();
app.promiseUsage();
}
private void promiseUsage() {
calculateLineCount();
calculateLowestFrequencyChar();
}
private void calculateLowestFrequencyChar() {
// Create a promise to calculate the lowest frequency character
lowestFrequencyChar().thenAccept(
charFrequency -> {
LOGGER.info("Char with lowest frequency is: {}", charFrequency);
}
);
}
private void calculateLineCount() {
// Create a promise to calculate the line count
countLines().thenAccept(
count -> {
LOGGER.info("Line count is: {}", count);
}
);
}
private Promise<Character> lowestFrequencyChar() {
// Create a promise to calculate the character frequency and then find the lowest frequency character
return characterFrequency().thenApply(Utility::lowestFrequencyChar);
}
private Promise<Map<Character, Long>> characterFrequency() {
// Create a promise to download a file and then calculate the character frequency
return download(DEFAULT_URL).thenApply(Utility::characterFrequency);
}
private Promise<Integer> countLines() {
// Create a promise to download a file and then count the lines
return download(DEFAULT_URL).thenApply(Utility::countLines);
}
private Promise<String> download(String urlString) {
// Create a promise to download a file
return new Promise<String>()
.fulfillInAsync(
() -> Utility.downloadFile(urlString), executor)
.onError(
throwable -> {
LOGGER.error("An error occurred: ", throwable);
}
);
}
}
在这段代码中,Promise
类用于创建各种操作的 Promise。thenApply
方法用于链接 Promise,这意味着一个 Promise 的结果被用作下一个 Promise 的输入。thenAccept
方法用于处理 Promise 的结果。fulfillInAsync
方法用于异步地完成 Promise,而 onError
方法用于处理完成 Promise 时发生的任何错误。
程序输出
08:19:33.036 [pool-1-thread-2] INFO com.iluwatar.promise.Utility -- Downloading contents from url: https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/promise/README.md
08:19:33.036 [pool-1-thread-1] INFO com.iluwatar.promise.Utility -- Downloading contents from url: https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/promise/README.md
08:19:33.419 [pool-1-thread-2] INFO com.iluwatar.promise.Utility -- File downloaded at: /var/folders/sg/9_st37nn5hq_bfhp8hw2dcrw0000gp/T/promise_pattern12403918065536844551.tmp
08:19:33.419 [pool-1-thread-1] INFO com.iluwatar.promise.Utility -- File downloaded at: /var/folders/sg/9_st37nn5hq_bfhp8hw2dcrw0000gp/T/promise_pattern11215446820862558571.tmp
08:19:33.419 [pool-1-thread-1] INFO com.iluwatar.promise.App -- Line count is: 164
08:19:33.426 [pool-1-thread-2] INFO com.iluwatar.promise.App -- Char with lowest frequency is: ’
何时在 Java 中使用 Promise 模式
- 当你需要执行异步任务并在稍后处理其结果或错误时。
- 在可以并行执行任务且需要在完成任务后处理其结果的场景中。
- 适合于提高异步代码的可读性和可维护性。
Promise 模式 Java 教程
- 使用 Java 8 的 CompletableFuture 进行函数式回调 (InfoQ)
- CompletableFuture 指南 (Baeldung)
- 你错过了 Promise 的关键 (Domenic Denicola)
Java 中 Promise 模式的实际应用
- Java 的 CompletableFuture 和 Future 类。
- JavaScript 的 Promise 对象,用于管理异步操作。
- 许多异步框架和库,例如 RxJava 和 Vert.x。
- Guava ListenableFuture
Promise 模式的优缺点
优点
- 提高可读性:简化复杂的异步代码,使其更易于理解和维护。
- 解耦:将启动异步操作的代码与处理结果的代码解耦。
- 错误处理:提供了一种统一的方式来处理异步操作的结果和错误。
缺点
- 复杂性:如果过度使用或使用不当,可能会增加代码库的复杂性。
- 调试:与同步代码相比,异步代码可能更难调试,因为执行流程是非线性的。
相关的 Java 设计模式
- 观察者:Promise 可以与观察者模式结合使用,以通知订阅者关于异步操作完成的情况。
- 回调:Promise 通常通过提供一种更结构化和可读的方式来处理异步结果来取代回调机制。
- 异步方法调用:Promise 通常用于处理异步方法调用的结果,从而实现非阻塞执行和结果处理。