Skip to content

Commit d72508c

Browse files
authored
Update README.md
1 parent 1b23c0a commit d72508c

File tree

1 file changed

+78
-8
lines changed

1 file changed

+78
-8
lines changed

README.md

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ There are several shortcomings associated with [CompletableFuture](https://docs.
99

1010
# How to use?
1111
Add Maven dependency
12-
```
12+
```xml
1313
<dependency>
1414
<groupId>net.tascalate.concurrent</groupId>
1515
<artifactId>net.tascalate.concurrent.lib</artifactId>
@@ -20,7 +20,7 @@ Add Maven dependency
2020
# What is inside?
2121
## 1. Promise interface
2222
The interface may be best described by the formula:
23-
```
23+
```java
2424
Promise == CompletionStage + Future
2525
```
2626

@@ -32,7 +32,7 @@ This is why this project was ever started. `CompletableTask` is the implementati
3232
a. `CompletableTask.asyncOn(Executor executor)`
3333

3434
Returns a resolved no-value `Promise` that is “bound” to the specified executor. I.e. any function passed to composition methods of `Promise` (like `thenApplyAsync` / `thenAcceptAsync` / `whenCompleteAsync` etc.) will be executed using this executor unless executor is overridden via explicit composition method parameter. Moreover, any nested composition calls will use same executor, if it’s not redefined via explicit composition method parameter:
35-
```
35+
```java
3636
CompletableTask
3737
.asyncOn(myExecutor)
3838
.thenApplyAsync(myValueGenerator)
@@ -44,7 +44,7 @@ All of `myValueGenerator`, `myConsumer`, `myActtion` will be executed using `myE
4444
b. `CompletableTask.complete(T value, Executor executor)`
4545

4646
Same as above, but the starting point is a resolved `Promise` with the specified value:
47-
```
47+
```java
4848
CompletableTask
4949
.complete("Hello!", myExecutor)
5050
.thenApplyAsync(myMapper)
@@ -56,7 +56,7 @@ All of `myMapper`, `myTransformer`, `myConsumer`, `myActtion` will be executed u
5656

5757
Additionally, you may submit [Supplier](https://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html) / [Runnable](https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) to the [Executor](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html) right away, in a similar way as with [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html):
5858

59-
```
59+
```java
6060
Promise<SomeValue> p1 = CompletableTask.supplyAsync(() -> {
6161
return blockingCalculationOfSomeValue();
6262
}, myExecutor);
@@ -65,7 +65,7 @@ Promise<Void> p2 = CompletableTask.runAsync(this::someIoBoundMethod, myExecutor)
6565
```
6666

6767
Most importantly, all composed promises support true cancellation (incl. interrupting thread) for the functions supplied as arguments:
68-
```
68+
```java
6969
Promise<?> p1 =
7070
CompletableTask
7171
.asyncOn(myExecutor)
@@ -78,7 +78,77 @@ p1.cancel(true);
7878
```
7979
In the example above `myConsumer` will be interrupted if already in progress. Both `p1` and `p2` will be resolved faulty: `p1` with a [CancellationException](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CancellationException.html) and `p2` with a [CompletionException](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionException.html).
8080

81-
## 3. Utility class Promises
81+
## 3. DependentPromise
82+
As it mentioned above, once you cancel `Promise`, all `Promise`-s that depends on this promise are completed with [CompletionException](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionException.html) wrapping[CancellationException](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CancellationException.html). This is a standard behavior, and [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) works just like this.
83+
84+
However, then you cancel derrived `Promise`, the original `Promise` is not cancelled:
85+
```java
86+
Promise<?> original = CompletableTask.supplyAsync(() -> someIoBoundMethod(), myExecutor);
87+
Promise<?> derrived = original.thenRunAsync(() -> someMethod() );
88+
...
89+
derrived.cancel(true);
90+
```
91+
So if you cancel `derrived` above it's [Runnable](https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html) method, wrapping `someMethod`, is interrupted. However `original` promise is not cancelled and `someIoBoundMethod` keeps running. This is not always a desired behavior, consider the following method:
92+
93+
```java
94+
public Promise<DataStructure> loadData(String url) {
95+
return CompletableTask.supplyAsync( () -> loadXml(url) ).thenApplyAsync( xml -> parseXml(xml) );
96+
}
97+
98+
...
99+
Promise<DataStructure> p = loadData("http://someserver.com/rest/ds");
100+
...
101+
if (someCondition()) {
102+
// Only second promise is canceled, parseXml.
103+
p.cancel(true);
104+
}
105+
```
106+
107+
Clients of this method see only derrived promise, and once they decide to cancel it, it is expected that any of `loadXml` and `parseXml` will be interrupted if not completed yet. To address this issue the library provides [DependentPromise](https://github.com/vsilaev/tascalate-concurrent/blob/master/src/main/java/net/tascalate/concurrent/DependentPromise.java) class:
108+
```java
109+
public Promise<DataStructure> loadData(String url) {
110+
return DependentPromise
111+
.from(CompletableTask.supplyAsync( () -> loadXml(url) ))
112+
.thenApplyAsync( xml -> parseXml(xml), true );
113+
}
114+
115+
...
116+
Promise<DataStructure> p = loadData("http://someserver.com/rest/ds");
117+
...
118+
if (someCondition()) {
119+
// Now the whole chain is canceled.
120+
p.cancel(true);
121+
}
122+
```
123+
[DependentPromise](https://github.com/vsilaev/tascalate-concurrent/blob/master/src/main/java/net/tascalate/concurrent/DependentPromise.java) overloads methods like `thenApply` / `thenRun` / `thenAccept` / `thenCombine` etc with additional argument:
124+
- if method accepts no other [CompletionStage](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html), like `thenApply` / `thenRun` / `thenAccept` etc, then it's a boolean flag `enlistOrigin` to specify whether or not original `Promise` should be enlisted for cancelation.
125+
- if method accepts other [CompletionStage](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html), like `thenCombine` / `applyToEither` / `thenAcceptBoth` etc, then it's a set of [PromiseOrigin](https://github.com/vsilaev/tascalate-concurrent/blob/master/src/main/java/net/tascalate/concurrent/PromiseOrigin.java) enum values, that specifies whether or not original `Promise` and/or `CompletionStage` supplied as argument should be enlisted for cancelation along with resulting promise, for example:
126+
127+
```java
128+
public Promise<DataStructure> loadData(String url) {
129+
return DependentPromise
130+
.from(CompletableTask.supplyAsync( () -> loadXml(url + "/source1") ))
131+
.thenCombine(
132+
CompletableTask.supplyAsync( () -> loadXml(url + "/source2") ),
133+
(xml1, xml2) -> Arrays.asList(xml1, xml2),
134+
PromiseOrigin.ALL
135+
) .
136+
.thenApplyAsync( xmls -> parseXmlsList(xmls), true );
137+
}
138+
```
139+
140+
Please note, then in release 0.5.4 there is a new default method `dependent` in interface [Promise](https://github.com/vsilaev/tascalate-concurrent/blob/master/src/main/java/net/tascalate/concurrent/Promise.java) that serves the same purpose and allows to write chained calls:
141+
142+
```java
143+
public Promise<DataStructure> loadData(String url) {
144+
return CompletableTask
145+
.supplyAsync( () -> loadXml(url) ))
146+
.dependent()
147+
.thenApplyAsync( xml -> parseXml(xml), true );
148+
}
149+
```
150+
151+
## 4. Utility class Promises
82152
The class
83153
provides convenient methods to combine several `CompletionStage`-s:
84154

@@ -115,7 +185,7 @@ Additionally, it's possible to convert to `Promise` API ready value:
115185

116186
`public static <T> Promise<T> from(CompletionStage<T> stage)`
117187

118-
## 4. Extensions to ExecutorService API
188+
## 5. Extensions to ExecutorService API
119189

120190
It’s not mandatory to use any specific subclasses of `Executor` with `CompletableTask` – you may use any implementation. However, someone may find beneficial to have a `Promise`-aware `ExecutorService` API. Below is a list of related classes/interfaces:
121191

0 commit comments

Comments
 (0)