1. Introduction
1.介绍
Core Java provides a basic API for asynchronous computations – Future. CompletableFuture is one of its newest implementations.
Core Java为异步计算提供了基本的API – Future. CompletableFuture是其最新的实现之一。
Vavr provides its new functional alternative to the Future API. In this article, we’ll discuss the new API and show how to make use of some of its new features.
Vavr提供了其新的功能性替代Future API。在这篇文章中,我们将讨论新的API,并展示如何利用其一些新功能。
More articles on Vavr can be found here.
关于Vavr的更多文章可以在这里找到。
2. Maven Dependency
2.Maven的依赖性
The Future API is included in the Vavr Maven dependency.
Future API包含在Vavr Maven依赖中。
So, let’s add it to our pom.xml:
所以,让我们把它添加到我们的pom.xml。
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>0.9.2</version>
</dependency>
We can find the latest version of the dependency on Maven Central.
我们可以在Maven Central上找到该依赖的最新版本。
3. Vavr’s Future
3.瓦夫尔的未来
The Future can be in one of two states:
未来可以处于两种状态之一:。
- Pending – the computation is ongoing
- Completed – the computation finished successfully with a result, failed with an exception or was canceled
The main advantage over the core Java Future is that we can easily register callbacks and compose operations in a non-blocking way.
与Java核心Future相比,其主要优势在于我们可以轻松注册回调,并以非阻塞的方式组合操作。
4. Basic Future Operations
4.基本的未来业务
4.1. Starting Asynchronous Computations
4.1.启动异步计算
Now, let’s see how we can start asynchronous computations using Vavr:
现在,让我们看看如何使用Vavr开始异步计算。
String initialValue = "Welcome to ";
Future<String> resultFuture = Future.of(() -> someComputation());
4.2. Retrieving Values from a Future
4.2.从一个未来中检索值
We can extract values from a Future by simply calling one of the get() or getOrElse() methods:
我们可以通过简单地调用get()或getOrElse()方法之一,从Future中提取值。
String result = resultFuture.getOrElse("Failed to get underlying value.");
The difference between get() and getOrElse() is that get() is the simplest solution, while getOrElse() enables us to return a value of any type in case we weren’t able to retrieve the value inside the Future.
get()和getOrElse()之间的区别是,get()是最简单的解决方案,而getOrElse()使我们能够返回一个任何类型的值,以防我们无法在Future内检索到该值。
It’s recommended to use getOrElse() so we can handle any errors that occur while trying to retrieve the value from a Future. For the sake of simplicity, we’ll just use get() in the next few examples.
我们建议使用getOrElse(),这样我们就可以处理在试图从Future中检索值时发生的任何错误。为了简单起见,我们将在接下来的几个例子中只使用get()。
Note that the get() method blocks the current thread if it’s necessary to wait for the result.
注意,如果需要等待结果,get()方法会阻塞当前线程。
A different approach is to call the nonblocking getValue() method, which returns an Option<Try<T>> which will be empty as long as computation is pending.
一个不同的方法是调用非阻塞的getValue()方法,该方法返回一个Option<Try<T>>,该方法只要计算还在进行,就会是空的。
We can then extract the computation result which is inside the Try object:
然后我们可以提取在Try对象中的计算结果。
Option<Try<String>> futureOption = resultFuture.getValue();
Try<String> futureTry = futureOption.get();
String result = futureTry.get();
Sometimes we need to check if the Future contains a value before retrieving values from it.
有时我们需要在从Future中检索值之前检查Future是否包含一个值。
We can simply do that by using:
我们可以简单地通过使用来做到这一点。
resultFuture.isEmpty();
It’s important to note that the method isEmpty() is blocking – it will block the thread until its operation is finished.
值得注意的是,方法isEmpty()是阻塞的–它将阻塞线程,直到其操作结束。
4.3. Changing the Default ExecutorService
4.3.改变默认的ExecutorService
Futures use an ExecutorService to run their computations asynchronously. The default ExecutorService is Executors.newCachedThreadPool().
Futures使用ExecutorService来异步运行其计算。默认的ExecutorService是Executors.newCachedThreadPool()。
We can use another ExecutorService by passing an implementation of our choice:
我们可以通过传递一个我们选择的实现来使用另一个ExecutorService。
@Test
public void whenChangeExecutorService_thenCorrect() {
String result = Future.of(newSingleThreadExecutor(), () -> HELLO)
.getOrElse(error);
assertThat(result)
.isEqualTo(HELLO);
}
5. Performing Actions Upon Completion
5.完成后执行行动
The API provides the onSuccess() method which performs an action as soon as the Future completes successfully.
API提供了onSuccess()方法,一旦Future成功完成,就会执行一个动作。
Similarly, the method onFailure() is executed upon the failure of the Future.
类似地,方法onFailure()在Future的失败时被执行。
Let’s see a quick example:
让我们看一个快速的例子。
Future<String> resultFuture = Future.of(() -> appendData(initialValue))
.onSuccess(v -> System.out.println("Successfully Completed - Result: " + v))
.onFailure(v -> System.out.println("Failed - Result: " + v));
The method onComplete() accepts an action to be run as soon as the Future has completed its execution, whether or not the Future was successful. The method andThen() is similar to onComplete() – it just guarantees the callbacks are executed in a specific order:
方法onComplete()接受一个动作,一旦Future完成其执行,无论Future是否成功,都将立即运行。方法andThen()与onComplete()类似 – 它只是保证回调以特定的顺序执行。
Future<String> resultFuture = Future.of(() -> appendData(initialValue))
.andThen(finalResult -> System.out.println("Completed - 1: " + finalResult))
.andThen(finalResult -> System.out.println("Completed - 2: " + finalResult));
6. Useful Operations on Futures
6.对期货的有用操作
6.1. Blocking the Current Thread
6.1.阻断当前线程
The method await() has two cases:
方法await()有两种情况。
- if the Future is pending, it blocks the current thread until the Future has completed
- if the Future is completed, it finishes immediately
Using this method is straightforward:
使用这种方法是很直接的。
resultFuture.await();
6.2. Canceling a Computation
6.2.取消计算
We can always cancel the computation:
我们可以随时取消计算。
resultFuture.cancel();
6.3. Retrieving the Underlying ExecutorService
6.3.检索底层的ExecutorService
To obtain the ExecutorService that is used by a Future, we can simply call executorService():
为了获得被Future使用的ExecutorService,我们可以简单地调用executorService()。
resultFuture.executorService();
6.4. Obtaining a Throwable from a Failed Future
6.4.从失败的Future中获取一个Throwable
We can do that using the getCause() method which returns the Throwable wrapped in an io.vavr.control.Option object.
我们可以使用getCause()方法来实现,该方法返回包裹在io.vavr.control.Option对象中的Throwable。
We can later extract the Throwable from the Option object:
我们以后可以从Option对象中提取Throwable。
@Test
public void whenDivideByZero_thenGetThrowable2() {
Future<Integer> resultFuture = Future.of(() -> 10 / 0)
.await();
assertThat(resultFuture.getCause().get().getMessage())
.isEqualTo("/ by zero");
}
Additionally, we can convert our instance to a Future holding a Throwable instance using the failed() method:
此外,我们可以使用failed()方法将我们的实例转换为一个持有Throwable实例的Future。
@Test
public void whenDivideByZero_thenGetThrowable1() {
Future<Integer> resultFuture = Future.of(() -> 10 / 0);
assertThatThrownBy(resultFuture::get)
.isInstanceOf(ArithmeticException.class);
}
6.5. isCompleted(), isSuccess(), and isFailure()
6.5.isCompleted()、isSuccess()、和isFailure()
These methods are pretty much self-explanatory. They check if a Future completed, whether it completed successfully or with a failure. All of them return boolean values, of course.
这些方法几乎是不言自明的。它们检查一个Future是否完成,是成功完成还是失败。当然,它们都会返回boolean值。
We’re going to use these methods with the previous example:
我们要把这些方法与前面的例子一起使用。
@Test
public void whenDivideByZero_thenCorrect() {
Future<Integer> resultFuture = Future.of(() -> 10 / 0)
.await();
assertThat(resultFuture.isCompleted()).isTrue();
assertThat(resultFuture.isSuccess()).isFalse();
assertThat(resultFuture.isFailure()).isTrue();
}
6.6. Applying Computations on Top of a Future
6.6.在未来的基础上应用计算
The map() method allows us to apply a computation on top of a pending Future:
map()方法允许我们在一个待定的Future:之上应用一个计算。
@Test
public void whenCallMap_thenCorrect() {
Future<String> futureResult = Future.of(() -> "from Baeldung")
.map(a -> "Hello " + a)
.await();
assertThat(futureResult.get())
.isEqualTo("Hello from Baeldung");
}
If we pass a function that returns a Future to the map() method, we can end up with a nested Future structure. To avoid this, we can leverage the flatMap() method:
如果我们将一个返回Future的函数传递给map()方法,我们最终会得到一个嵌套的Future结构。为了避免这种情况,我们可以利用flatMap()方法。
@Test
public void whenCallFlatMap_thenCorrect() {
Future<Object> futureMap = Future.of(() -> 1)
.flatMap((i) -> Future.of(() -> "Hello: " + i));
assertThat(futureMap.get()).isEqualTo("Hello: 1");
}
6.7. Transforming Futures
6.7.改造期货
The method transformValue() can be used to apply a computation on top of a Future and change the value inside it to another value of the same type or a different type:
方法transformValue()可用于在Future之上应用一个计算,并将其内部的值改变为相同类型或不同类型的另一个值。
@Test
public void whenTransform_thenCorrect() {
Future<Object> future = Future.of(() -> 5)
.transformValue(result -> Try.of(() -> HELLO + result.get()));
assertThat(future.get()).isEqualTo(HELLO + 5);
}
6.8. Zipping Futures
6.8.拉链期货
The API provides the zip() method which zips Futures together into tuples – a tuple is a collection of several elements that may or may not be related to each other. They can also be of different types. Let’s see a quick example:
API提供了zip()方法,该方法将Futures压缩成图元–图元是若干元素的集合,这些元素可能相互关联,也可能相互不关联。它们也可以是不同的类型。让我们看一个快速的例子。
@Test
public void whenCallZip_thenCorrect() {
Future<String> f1 = Future.of(() -> "hello1");
Future<String> f2 = Future.of(() -> "hello2");
assertThat(f1.zip(f2).get())
.isEqualTo(Tuple.of("hello1", "hello2"));
}
The point to note here is that the resulting Future will be pending as long as at least one of the base Futures is still pending.
这里需要注意的是,只要至少有一个基本的Futures还在等待中,所产生的Future就会被挂起。
6.9. Conversion Between Futures and CompletableFutures
6.9.期货和可完成期货之间的转换
The API supports integration with java.util.CompletableFuture. So, we can easily convert a Future to a CompletableFuture if we want to perform operations that only the core Java API supports.
该API支持与java.util.CompletableFuture的集成。因此,如果我们想执行只有核心Java API支持的操作,我们可以轻松地将Future转换为CompletableFuture。
Let’s see how we can do that:
让我们看看我们如何能够做到这一点。
@Test
public void whenConvertToCompletableFuture_thenCorrect()
throws Exception {
CompletableFuture<String> convertedFuture = Future.of(() -> HELLO)
.toCompletableFuture();
assertThat(convertedFuture.get())
.isEqualTo(HELLO);
}
We can also convert a CompletableFuture to a Future using the fromCompletableFuture() method.
我们还可以使用fromCompletableFuture()方法将CompletableFuture转换为Future。
6.10. Exception Handling
6.10.异常处理
Upon the failure of a Future, we can handle the error in a few ways.
当一个Future失败时,我们可以通过几种方式来处理错误。
For example, we can make use of the method recover() to return another result, such as an error message:
例如,我们可以利用recover()方法来返回另一个结果,比如说错误信息。
@Test
public void whenFutureFails_thenGetErrorMessage() {
Future<String> future = Future.of(() -> "Hello".substring(-1))
.recover(x -> "fallback value");
assertThat(future.get())
.isEqualTo("fallback value");
}
Or, we can return the result of another Future computation using recoverWith():
或者,我们可以使用recoverWith()返回另一个Future计算的结果。
@Test
public void whenFutureFails_thenGetAnotherFuture() {
Future<String> future = Future.of(() -> "Hello".substring(-1))
.recoverWith(x -> Future.of(() -> "fallback value"));
assertThat(future.get())
.isEqualTo("fallback value");
}
The method fallbackTo() is another way to handle errors. It’s called on a Future and accepts another Future as a parameter.
方法fallbackTo()是另一种处理错误的方法。它在一个Future上被调用,并接受另一个Future作为参数。
If the first Future is successful, then it returns its result. Otherwise, if the second Future is successful, then it returns its result. If both Futures fail, then the failed() method returns a Future of a Throwable, which holds the error of the first Future:
如果第一个Future成功了,那么它将返回其结果。否则,如果第二个Future是成功的,那么它将返回其结果。如果两个Futures都失败了,那么failed()方法就会返回一个Future的Throwable,它持有第一个Future>的错误。
@Test
public void whenBothFuturesFail_thenGetErrorMessage() {
Future<String> f1 = Future.of(() -> "Hello".substring(-1));
Future<String> f2 = Future.of(() -> "Hello".substring(-2));
Future<String> errorMessageFuture = f1.fallbackTo(f2);
Future<Throwable> errorMessage = errorMessageFuture.failed();
assertThat(
errorMessage.get().getMessage())
.isEqualTo("String index out of range: -1");
}
7. Conclusion
7.结论
In this article, we’ve seen what a Future is and learned some of its important concepts. We’ve also walked through some of the features of the API using a few practical examples.
在这篇文章中,我们已经看到了什么是Future,并了解了它的一些重要概念。我们还通过一些实际的例子了解了该API的一些特性。
The full version of the code is available over on GitHub.
完整版本的代码可在GitHub上获得。