1. Introduction
1.导言
Future and Promise are tools used to handle asynchronous tasks, allowing one to execute operations without waiting for each step to complete. Although they both serve the same purpose, they exhibit key differences. In this tutorial, we’ll explore the differences between Future and Promise, scrutinizing their key characteristics, use cases, and distinctive features.
Future 和 Promise 是用于处理异步任务的工具,允许执行操作而无需等待每一步完成。虽然两者的目的相同,但它们之间存在关键的差异。在本教程中,我们将探讨 Future 和 Promise 之间的差异,研究它们的关键特性、用例和独特功能。
2. Understanding Future
2) 了解未来
Future acts as a container, awaiting the outcome of ongoing operations. Developers commonly employ Future to check the status of computations, retrieve results upon readiness, or gracefully wait until the operations conclude. Future often integrates with the Executor framework, providing a straightforward and efficient approach to handling asynchronous tasks.
Future 充当容器,等待正在进行的操作的结果。开发人员通常使用 Future 来检查计算的状态、在准备就绪时检索结果或优雅地等待操作结束。
2.1. Key Characteristics
2.1.主要特点
Now, let’s explore some of Future‘s key characteristics:
现在,让我们来探讨一下 Future 的一些主要特点:
- Adopt a blocking design, which potentially leads to a wait until the asynchronous computation is complete.
- Direct interaction with the ongoing computation is restricted, maintaining a straightforward approach.
2.2. Use Cases
2.2.使用案例
Future excels in scenarios where the result of an asynchronous operation is predetermined and cannot be altered once the process begins.
Future适用于异步操作的结果已预先确定且流程开始后无法更改的场景。
Consider fetching a user’s profile information from a database or downloading a file from a remote server. Once initiated, these operations have a fixed outcome, such as the data retrieved or the file downloaded, and cannot be modified mid-process.
考虑从数据库中获取用户配置文件信息或从远程服务器下载文件。这些操作一旦启动,就会有一个固定的结果,如检索到的数据或下载的文件,并且不能中途修改。
2.3. Using Future
2.3.使用 Future
To utilize Future, we can find them residing in the java.util.concurrent package. Let’s look at a code snippet demonstrating how to employ a Future for asynchronous task handling:
要使用 Future,我们可以在 java.util.concurrent 包中找到它们。让我们来看一段代码,演示如何使用 Future 进行异步任务处理:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> futureResult = executorService.submit(() -> {
Thread.sleep(2000);
return "Future Result";
});
while (!futureResult.isDone()) {
System.out.println("Future task is still in progress...");
Thread.sleep(500);
}
String resultFromFuture = futureResult.get();
System.out.println("Future Result: " + resultFromFuture);
executorService.shutdown();
And let’s check the output we get when we run the code:
让我们检查一下运行代码时的输出结果:
Future task is still in progress...
Future task is still in progress...
Future task is still in progress...
Future task is still in progress...
Future Result: Future Result
In the code, the futureResult.get() method is a blocking call. This means that when the program reaches this line, it will wait until the asynchronous task submitted to the ExecutorService is complete before moving on.
在代码中,futureResult.get() 方法是一个阻塞调用。这意味着当程序运行到这一行时,它将等待提交给 ExecutorService 的异步任务完成后再继续运行。
3. Understanding Promise
3.了解承诺
In contrast, the concept of a Promise is not native to Java but is a versatile abstraction in other programming languages. A Promise acts as a proxy for a value that might not be known when the Promise is created. Unlike Future, Promise often provides a more interactive approach, allowing developers to influence the asynchronous computation even after its initiation.
相比之下,Promise 的概念并非 Java 的原生概念,而是其他编程语言中的通用抽象概念。<与 Future 不同,Promise 通常提供一种交互性更强的方法,允许开发人员在异步计算启动后对其施加影响。
3.1. Key Characteristics
3.1.主要特点
Now, let’s explore some of Promise’s key characteristics:
现在,让我们来探讨一下 Promise 的一些主要特点:
- Encapsulates a mutable state, permitting modification even after the asynchronous operation has begun, providing flexibility in handling dynamic scenarios
- Employ a callback mechanism, allowing developers to attach callbacks executed upon completion, failure, or progress of the asynchronous operation
3.2. Use Cases
3.2.使用案例
Promise is well-suited for scenarios where dynamic and interactive control over asynchronous operations is essential. Furthermore, Promise offers flexibility in modifying the ongoing computation even after initiation. A good example of this would be streaming real-time data in financial applications where the display content needs to adapt to live market changes.
Promise 非常适合需要对异步操作进行动态和交互式控制的应用场景。此外,即使在启动后,Promise 也能灵活地修改正在进行的计算。金融应用中的流式实时数据就是一个很好的例子,在这种应用中,显示内容需要适应实时市场变化。
Moreover, Promise is beneficial when dealing with asynchronous tasks that require conditional branching or modifications based on intermediate results. One possible use case is when we need to handle multiple asynchronous API calls where subsequent operations depend on the outcomes of previous ones.
此外,在处理需要根据中间结果进行有条件分支或修改的异步任务时,Promise 也非常有用。一个可能的用例是,当我们需要处理多个异步 API 调用时,后续操作取决于之前操作的结果。
3.3. Using Promise
3.3.使用承诺
Java might not have a dedicated Promise class that strictly adheres to the Promise specification as in JavaScript. However, we can achieve similar functionality using java.util.concurrent.CompletableFuture. CompletableFuture provides a versatile way to work with asynchronous tasks, sharing some characteristics with Promise. It’s important to note that they are not the same.
不过,我们可以使用 java.util.concurrent.CompletableFuture来实现类似的功能。需要注意的是,它们并不相同。
Let’s explore how to use CompletableFuture to achieve Promise-like behavior in Java:
让我们探索如何使用 CompletableFuture 在 Java 中实现类似 Promise 的行为:
ExecutorService executorService = Executors.newSingleThreadExecutor();
CompletableFuture<String> completableFutureResult = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "CompletableFuture Result";
}, executorService);
completableFutureResult.thenAccept(result -> {
System.out.println("Promise Result: " + result);
})
.exceptionally(throwable -> {
System.err.println("Error occurred: " + throwable.getMessage());
return null;
});
System.out.println("Doing other tasks...");
executorService.shutdown();
When we run the code, we’ll see the output:
运行代码后,我们将看到输出结果:
Doing other tasks...
Promise Result: CompletableFuture Result
We create a CompletableFuture named completableFutureResult. The supplyAsync() method is used to initiate an asynchronous computation. The provided lambda function represents the asynchronous task.
我们创建了一个名为 completableFutureResult 的 CompletableFuture 。supplyAsync() 方法用于启动异步计算。提供的 lambda 函数表示异步任务。
Next, we attach callbacks to the CompletableFuture using thenAccept() and exceptionally(). The thenAccept() callback handles the successful completion of the asynchronous task, similar to the resolution of a Promise, while exceptionally() handles any exceptions that might occur during the task, resembling the rejection of a Promise.
接下来,我们使用 thenAccept() 和 exceptionally() 为 CompletableFuture 附加回调。thenAccept() 回调处理异步任务的成功完成,类似于 Promise 的解析,而 exceptionally() 则处理任务期间可能发生的任何异常,类似于 Promise 的拒绝。
4. Key Differences
4.主要差异
4.1. Control Flow
4.1 控制流
Once a Future‘s value is set, the control flow proceeds downstream, unaffected by subsequent events or changes. Meanwhile, Promise (or CompletableFuture) provides methods like thenCompose() and whenComplete() for conditional execution based on the final result or exceptions.
一旦设置了 Future 的值,控制流就会顺流而下,不受后续事件或更改的影响。同时,Promise(或CompletableFuture)提供了thenCompose()和whenComplete()等方法,用于根据最终结果或异常情况有条件地执行。
Let’s create an example of branching control flow using CompletableFuture:
让我们使用 CompletableFuture 创建一个分支控制流示例:
CompletableFuture<Integer> firstTask = CompletableFuture.supplyAsync(() -> {
return 1;
})
.thenApplyAsync(result -> {
return result * 2;
})
.whenComplete((result, ex) -> {
if (ex != null) {
// handle error here
}
});
In the code, we use the thenApplyAsync() method to demonstrate the chaining of asynchronous tasks.
在代码中,我们使用 thenApplyAsync() 方法来演示异步任务链。
4.2. Error Handling
4.2 错误处理
Both Future and Promise provide mechanisms for handling errors and exceptions. Future relies on exceptions thrown during the computation:
Future 和 Promise 都提供了处理错误和异常的机制。Future 依赖于计算过程中抛出的异常:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> futureWithError = executorService.submit(() -> {
throw new RuntimeException("An error occurred");
});
try {
String result = futureWithError.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
In CompletableFuture, the exceptionally() method is used to handle any exception that occurs during the asynchronous computation. If an exception occurs, it prints an error message and provides a fallback value:
在 CompletableFuture 中,exceptionally() 方法用于处理异步计算过程中出现的任何异常。如果出现异常,该方法会打印错误信息并提供一个后备值:
CompletableFuture<String> promiseWithError = new CompletableFuture<>();
promiseWithError.completeExceptionally(new RuntimeException("An error occurred"));
promiseWithError.exceptionally(throwable -> {
return "Fallback value";
});
4.3. Read-Write Access
4.3.读写访问
Future provides a read-only view, allowing us to retrieve the result once the computation is complete:
Future 提供只读视图,允许我们在计算完成后检索结果:
Future<Integer> future = executor.submit(() -> 100);
// Cannot modify future.get() after completion
In contrast, CompletableFuture enables us not only to read the result but also to actively set values dynamically even after the asynchronous operation has started:
相比之下,CompletableFuture 使我们不仅能读取结果,还能在异步操作开始后动态地主动设置值:
ExecutorService executorService = Executors.newSingleThreadExecutor();
CompletableFuture<Integer> totalPromise = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 100;
}, executorService);
totalPromise.thenAccept(value -> System.out.println("Total $" + value ));
totalPromise.complete(10);
Initially, we set up the asynchronous task to return 100 as the result. However, we intervene and explicitly complete the task with the value 10 before it completes naturally. This flexibility highlights the write-capable nature of CompletableFuture, allowing us to dynamically update the result during asynchronous execution.
最初,我们设置异步任务的结果为 100。但是,我们进行了干预,并在任务自然完成之前显式地以 10 的值完成了任务。这种灵活性突出了 CompletableFuture 的可写特性,允许我们在异步执行期间动态更新结果。
5. Conclusion
5.结论
In this article, we’ve explored the distinction between Future and Promise. While both serve the purpose of handling asynchronous tasks, they differ significantly in their capabilities.
在本文中,我们探讨了 Future 和 Promise 之间的区别。虽然两者都以处理异步任务为目的,但它们在功能上有很大不同。
As always, the source code for the examples is available over on GitHub.
与往常一样,这些示例的源代码可在 GitHub 上获取。