Skip to content

Commit b944b31

Browse files
committed
Restructure text
1 parent 8f9b393 commit b944b31

13 files changed

+680
-234
lines changed

Diff for: README.md

+270-220
Large diffs are not rendered by default.

Diff for: api.md

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# The Java 8 API
2+
CompletableFuture has the following static methods:
3+
* `supplyAsync(Supplier<T>)` and `supplyAsync(Supplier<T>, Executor)` - Create a future from code that runs on a different thread
4+
* `runAsync(Runnable, Executor)` and `runAsync(Runnable)` - Similar to supplyAsync, but returns a void future
5+
* `completedFuture(T)` - Returns a future that's already complete
6+
* `allOf(CompletableFuture...)` - A future that completes when all futures complete
7+
* `anyOf(CompletableFuture...)` - Similar to allOf, but returns as soon as any of the futures complete
8+
9+
Methods for introspecting the state of a future:
10+
* `isDone()`, `isCancelled()` and `isCompletedExceptionally()` - Self-explanatory?
11+
* `getNumberOfDependents()` - Estimated number of callbacks for future completion (For monitoring only!)
12+
13+
Methods for extracting the value of a future:
14+
* `getNow(T default)` - Non-blocking - returns the default value if it is not complete.
15+
* `get()` - Blocking get - throws checked exceptions
16+
* `get(long, TimeUnit)` - Blocking get, but with a timeout
17+
* `join()` - Blocking get - throws unchecked exceptions
18+
19+
Methods for setting the state of the future
20+
* `complete(Object)`, `completeExceptionally(Throwable)` and `cancel(boolean)` - Complete the future in various ways
21+
* `obtrudeValue(Object)` and `obtrudeException(Throwable)` - Complete the future, mutating the value it even if it is already completed!
22+
23+
It has a bunch of methods that operate on a future to create a new (and improved!) future:
24+
* `thenApply(Function)` - transform value -> value
25+
* `thenApplyAsync(Function, Executor)` -
26+
* `thenApplyAsync(Function)` -
27+
* `thenAccept(Consumer)` -
28+
* `thenAcceptAsync(Consumer, Executor)` -
29+
* `thenAcceptAsync(Consumer)` -
30+
* `thenRun(Runnable)` -
31+
* `thenRunAsync(Runnable, Executor)` -
32+
* `thenRunAsync(Runnable)` -
33+
* `thenCombine(CompletionStage, BiFunction)` -
34+
* `thenCombineAsync(CompletionStage, BiFunction, Executor)` -
35+
* `thenCombineAsync(CompletionStage, BiFunction)` -
36+
* `thenAcceptBoth(CompletionStage, BiConsumer)` -
37+
* `thenAcceptBothAsync(CompletionStage, BiConsumer, Executor)` -
38+
* `thenAcceptBothAsync(CompletionStage, BiConsumer)` -
39+
* `runAfterBoth(CompletionStage, Runnable)` -
40+
* `runAfterBothAsync(CompletionStage, Runnable, Executor)` -
41+
* `runAfterBothAsync(CompletionStage, Runnable)` -
42+
* `applyToEither(CompletionStage, Function)` -
43+
* `applyToEitherAsync(CompletionStage, Function, Executor)` -
44+
* `applyToEitherAsync(CompletionStage, Function)` -
45+
* `acceptEither(CompletionStage, Consumer)` -
46+
* `acceptEitherAsync(CompletionStage, Consumer, Executor)` -
47+
* `acceptEitherAsync(CompletionStage, Consumer)` -
48+
* `runAfterEither(CompletionStage, Runnable)` -
49+
* `runAfterEitherAsync(CompletionStage, Runnable, Executor)` -
50+
* `runAfterEitherAsync(CompletionStage, Runnable)` -
51+
* `thenCompose(Function)` - transform value -> CompletionStage(value)
52+
* `thenComposeAsync(Function, Executor)` -
53+
* `thenComposeAsync(Function)` -
54+
* `whenComplete(BiConsumer)` -
55+
* `whenCompleteAsync(BiConsumer, Executor)` -
56+
* `whenCompleteAsync(BiConsumer)` -
57+
* `handle(BiFunction)` - transform either value or exception
58+
* `handleAsync(BiFunction, Executor)` -
59+
* `handleAsync(BiFunction)` -
60+
* `exceptionally(Function)` - transform exception
61+
62+
There are a lot of methods here - most of them can be expressed in terms of `handle` and `thenCompose`
63+
and can be considered convenience methods and to better express intent.
64+
65+
Some of them can be considered callbacks instead of transforms, but they still return futures so you can
66+
take actions on the completion, even if you don't care about their values.
67+
68+
# News in Java 9
69+
70+
With Java 9 we got some additions to the API.
71+
72+
New static methods:
73+
* `delayedExecutor(long, TimeUnit)` and `delayedExecutor(long, TimeUnit, Executor)` - Get an executor that delays the work
74+
* `completedStage(Object)`, `failedFuture(Throwable)` and `failedStage(Throwable)` - Complements for the `completedFuture(Object)` method
75+
76+
New instance methods:
77+
* `newIncompleteFuture()` - Virtual constructor, intended for subclassing
78+
* `defaultExecutor()` - Get the default executor that is used for default `*Async` transforms.
79+
* `copy()` - Convenience method for `thenApply(x -> x)`
80+
* `minimalCompletionStage()` - Returns a more restricted future
81+
* `completeAsync(Supplier, Executor)` and `completeAsync(Supplier)` - Complete the future using a supplier
82+
* `completeOnTimeout(Object, long, TimeUnit)` - Complete the future with a value after a timeout
83+
* `orTimeout(long, TimeUnit)` - Complete the future with an exception after a timeout
84+
85+
# Changes between Java 10 and Java 15
86+
87+
Nothing has changed - this API seems to have stabilized.

Diff for: futures-concept.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
The concept of futures is not limited to asynchronous programming in Java.
2+
This [wikipedia article](https://en.wikipedia.org/wiki/Futures_and_promises) has more details about it.
3+
4+
## Producers and consumers
5+
6+
You can think of futures from two sides - producers and consumers.
7+
8+
We usually spend most of the time using futures as consumers, and then we usually seem them as read-only containers
9+
that change state once. We can subscribe to be notified when the state changes and trigger further work.
10+
11+
From a producer side, we should instead use the term Promise. You create a _promise_ that you will produce a value
12+
and some time later when the value is ready, you complete or fulfill the promise. The future associated with the promise
13+
will thus complete and the consumer can act on it.
14+
15+
You can think of it as the following flow:
16+
* The producer creates a Promise
17+
* The produces extracts the Future from the Promise and gives the Future to the consumer.
18+
* The consumer starts registering callbacks and transformations on the future.
19+
* At some point later the producer fulfills the Promise and the callbacks will trigger for the consumer.
20+

Diff for: history.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# A brief history of futures in Java
2+
3+
Java 1.5 introduced the `Future` interface but it was a very limited - you could call `get()` and `isDone()`
4+
but there was no way to get notified of state changes. Instead you would need to do polling.
5+
6+
Google Guava introduced the `ListenableFuture` interface which extends `Future` but also adds
7+
`addListener(Runnable, executor)` - this one method enabled a more asynchronous development model.
8+
All other useful methods could now be implemented as utility functions on top of the primitives.
9+
10+
The Google Guava class `Futures` included some very useful methods:
11+
* `addCallback(callback)` - convenience method on top of `addListener`
12+
* `transform(function)` - return a new future after applying a function to the values
13+
* `transformAsync(function)` - like transform, but the function should return a future instead.
14+
(Similar to Java 8 `thenCompose`)
15+
16+
Then with Java 8 we got `CompletableFuture` which had achieved feature parity with Google Guava futures,
17+
though with differences in:
18+
* Fluent API - `CompletableFuture` has a fluent API unlike `ListenableFuture` (but Google later added `FluentFuture` too)
19+
* Mutability - Google futures separates the producer side from the consumer side, while they are much more tightly coupled
20+
in `CompletableFuture`.
21+
* Number of primitives - Google futures have a small set of primitives (transform, transformAsync, catching) that
22+
can be combined while `CompletableFuture` has a large set of methods for combinations of the underlying primitives.
23+
thenApply, thenRun and thenAccept could be reduced to a single promitive and likewise for
24+
handle, runAfter, runAsync, whenComplete. Almost all methods in `CompletableFuture` also exist in three variants:
25+
regular, async with default executor and async with custom executor.

Diff for: library.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Completable-futures library
2+
3+
To simplify life working with Java 8+ futures, there's a Spotify library called
4+
[completable-futures](https://github.com/spotify/completable-futures).
5+
6+
It contains some useful utility methods, described in more detail below
7+
8+
## dereference
9+
10+
Sometimes (through no fault of you own, I'm sure!) you may end up with something like:
11+
`CompletableFuture<CompletableFuture<T>> future` which may be annoying to work with.
12+
13+
Fortunately, it's not very difficult to convert that to `CompletableFuture<T> future2`.
14+
All you need to do is apply `thenCompose(value -> value)` (composing with the identity function).
15+
16+
This has been wrapped as `dereference` - naming was chosen to correspond to the equivalent function in Google Guava.
17+
18+
## getCompleted and getException
19+
20+
These functions can simplify getting the value or exception from a completed future, without risking blocking the thread.
21+
22+
This is typically useful in code that needs the value from known completed futures or in unit tests.
23+
24+
## exceptionallyCompose
25+
26+
`exceptionallyCompose()` was introduced to cover the usecase of handling a failure by doing more asynchronous work.
27+
While the Java 8 API lets you handle a successful future and compose it (with `thenCompose`) there is no such
28+
equivalent for handling exception. This convenience method is very simple to implement.
29+
30+
> **_Implementation:_** [ExceptionallyCompose](src/test/java/se/krka/futures/ExceptionallyCompose.java)
31+
32+
## combine
33+
34+
It also includes some utility functions to combine multiple futures in a safe way, avoiding manual error-prone calls
35+
to get or join.
36+
37+
## And more...
38+
39+
Check out the github page to learn about more utilities.

Diff for: src/main/java/se/krka/futures/PFuture.java

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package se.krka.futures;
2+
3+
import java.util.concurrent.CancellationException;
4+
import java.util.concurrent.CompletableFuture;
5+
import java.util.concurrent.CompletionException;
6+
import java.util.function.Consumer;
7+
import java.util.function.Function;
8+
9+
public class PFuture<T> {
10+
private final CompletableFuture<T> future;
11+
12+
PFuture(CompletableFuture<T> future) {
13+
this.future = future;
14+
}
15+
16+
public <R> PFuture<R> map(Function<T, R> function) {
17+
return new PFuture<>(future.thenApply(function));
18+
}
19+
20+
public PFuture<T> tap(Consumer<T> function) {
21+
return new PFuture<>(future.thenApply(t -> {
22+
function.accept(t);
23+
return t;
24+
}));
25+
}
26+
27+
public <R> PFuture<R> flatMap(Function<T, PFuture<R>> function) {
28+
return new PFuture<>(future.thenCompose(t -> function.apply(t).future));
29+
}
30+
31+
public static <R> PFuture<R> flatten(PFuture<PFuture<R>> future) {
32+
return new PFuture<>(future.future.thenCompose(v -> v.future));
33+
}
34+
35+
public boolean isDone() {
36+
return future.isDone();
37+
}
38+
39+
public boolean isCompletedExceptionally() {
40+
return future.isCompletedExceptionally();
41+
}
42+
43+
public boolean isCompletedNormally() {
44+
return future.isDone() && !future.isCompletedExceptionally();
45+
}
46+
47+
48+
public Throwable getException() {
49+
if (isCompletedExceptionally()) {
50+
try {
51+
T value = future.join();
52+
assert value != null;
53+
} catch (CancellationException e) {
54+
return e;
55+
} catch (CompletionException e) {
56+
return e.getCause();
57+
}
58+
throw new IllegalStateException("Unreachable code");
59+
} else {
60+
throw new IllegalStateException("Future does not have an exception");
61+
}
62+
}
63+
64+
public T getValue() {
65+
if (isCompletedNormally()) {
66+
return future.join();
67+
} else {
68+
throw new IllegalStateException("Future does not have a value");
69+
}
70+
}
71+
72+
@Override
73+
public String toString() {
74+
if (isCompletedExceptionally()) {
75+
return "PFuture(exception=" + getException().getMessage() + ")";
76+
} else if (isCompletedNormally()) {
77+
return "PFuture(value=" + getValue() + ")";
78+
} else {
79+
return "PFuture(incomplete)";
80+
}
81+
}
82+
}

Diff for: src/main/java/se/krka/futures/Promise.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void cancel() {
2929
producerFuture.cancel(false);
3030
}
3131

32-
public CompletableFuture<T> getFuture() {
33-
return producerFuture.thenApply(Function.identity());
32+
public PFuture<T> getFuture() {
33+
return new PFuture<>(producerFuture);
3434
}
3535
}

Diff for: src/test/java/se/krka/futures/CancelTest.java

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.concurrent.CompletableFuture;
88
import java.util.concurrent.CompletionException;
99

10+
import static org.junit.Assert.assertEquals;
1011
import static org.junit.Assert.assertFalse;
1112
import static org.junit.Assert.assertTrue;
1213

@@ -15,13 +16,17 @@ public class CancelTest {
1516
public void cancelSimple() {
1617
CompletableFuture<?> future = new CompletableFuture<>();
1718
future.cancel(true);
19+
20+
assertEquals(CancellationException.class, Util.exceptionFromCallback(future).getClass());
1821
Util.assertException(future, List.of(CancellationException.class));
1922
}
2023

2124
@Test
2225
public void cancelWithException() {
2326
CompletableFuture<?> future = new CompletableFuture<>();
2427
future.completeExceptionally(new CancellationException());
28+
29+
assertEquals(CancellationException.class, Util.exceptionFromCallback(future).getClass());
2530
Util.assertException(future, List.of(CancellationException.class));
2631
}
2732

@@ -39,6 +44,9 @@ public void cancelParent() {
3944
assertFalse(future2.isCancelled()); // This was NOT explicitly cancelled!
4045
assertTrue(future2.isCompletedExceptionally());
4146

47+
assertEquals(CancellationException.class, Util.exceptionFromCallback(future).getClass());
48+
assertEquals(CompletionException.class, Util.exceptionFromCallback(future2).getClass());
49+
4250
Util.assertException(future, List.of(CancellationException.class));
4351
Util.assertException(future2, List.of(CompletionException.class, CancellationException.class));
4452
}
@@ -57,7 +65,9 @@ public void cancelChild() {
5765
assertTrue(future2.isCancelled());
5866
assertTrue(future2.isCompletedExceptionally());
5967

68+
assertEquals(CancellationException.class, Util.exceptionFromCallback(future2).getClass());
6069
Util.assertException(future2, List.of(CancellationException.class));
6170

6271
}
72+
6373
}

Diff for: src/test/java/se/krka/futures/ExceptionTest.java

+47-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import java.util.concurrent.CancellationException;
88
import java.util.concurrent.CompletableFuture;
99
import java.util.concurrent.CompletionException;
10+
import java.util.concurrent.TimeoutException;
11+
12+
import static org.junit.Assert.assertEquals;
1013

1114
public class ExceptionTest {
1215

@@ -15,9 +18,35 @@ public class ExceptionTest {
1518
IllegalArgumentException.class
1619
);
1720

21+
@Test
22+
public void testExceptionSimple() {
23+
CompletableFuture<Object> future = new CompletableFuture<>();
24+
future.completeExceptionally(new IllegalArgumentException());
25+
assertEquals(IllegalArgumentException.class, Util.exceptionFromCallback(future).getClass());
26+
Util.assertException(future, List.of(CompletionException.class, IllegalArgumentException.class));
27+
28+
// Wrapped, since it has gotten a transform operation!
29+
CompletableFuture<Object> future2 = future.thenApply(x -> x);
30+
assertEquals(CompletionException.class, Util.exceptionFromCallback(future2).getClass());
31+
Util.assertException(future2, List.of(CompletionException.class, IllegalArgumentException.class));
32+
33+
// Same for rethrowing the exception in a transform
34+
CompletableFuture<Object> future3 = future.exceptionally(e -> Util.doThrow((RuntimeException) e));
35+
assertEquals(CompletionException.class, Util.exceptionFromCallback(future3).getClass());
36+
Util.assertException(future3, List.of(CompletionException.class, IllegalArgumentException.class));
37+
38+
// Same for doing a copy of the future
39+
CompletableFuture<Object> future4 = future.copy();
40+
assertEquals(CompletionException.class, Util.exceptionFromCallback(future4).getClass());
41+
Util.assertException(future4, List.of(CompletionException.class, IllegalArgumentException.class));
42+
43+
}
44+
1845
@Test
1946
public void testExceptionTypeSupply() {
20-
Util.assertException(CompletableFuture.supplyAsync(() -> Util.doThrow(new IllegalArgumentException())), EXPECTED);
47+
CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> Util.doThrow(new IllegalArgumentException()));
48+
Util.assertException(future, EXPECTED);
49+
assertEquals(CompletionException.class, Util.exceptionFromCallback(future).getClass());
2150
}
2251

2352
@Test
@@ -60,7 +89,24 @@ public void testExceptionTypeComposeThrowWrapped() {
6089
public void testCancelException() {
6190
CompletableFuture<Object> future = new CompletableFuture<>();
6291
future.cancel(true);
92+
assertEquals(CancellationException.class, Util.exceptionFromCallback(future).getClass());
6393
Util.assertException(future, List.of(CancellationException.class));
6494
}
6595

96+
@Test
97+
public void testCancelException2() {
98+
CompletableFuture<Object> future = new CompletableFuture<>();
99+
future.completeExceptionally(new CancellationException());
100+
assertEquals(CancellationException.class, Util.exceptionFromCallback(future).getClass());
101+
Util.assertException(future, List.of(CancellationException.class));
102+
}
103+
104+
@Test
105+
public void testTimeoutException() {
106+
CompletableFuture<Object> future = new CompletableFuture<>();
107+
future.completeExceptionally(new TimeoutException());
108+
assertEquals(TimeoutException.class, Util.exceptionFromCallback(future).getClass());
109+
Util.assertException(future, List.of(CompletionException.class, TimeoutException.class));
110+
}
111+
66112
}

0 commit comments

Comments
 (0)