Skip to content

Commit 6e22be3

Browse files
committed
docs: update promise
1 parent 279d227 commit 6e22be3

File tree

5 files changed

+89
-238
lines changed

5 files changed

+89
-238
lines changed

promise/README.md

Lines changed: 82 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -3,309 +3,162 @@ title: Promise
33
category: Concurrency
44
language: en
55
tag:
6-
- Reactive
6+
- Asynchronous
7+
- Decoupling
8+
- Messaging
9+
- Synchronization
10+
- Thread management
711
---
812

913
## Also known as
1014

11-
CompletableFuture
15+
* Deferred
16+
* Future
1217

1318
## Intent
1419

15-
A Promise represents a proxy for a value not necessarily known when the promise is created. It
16-
allows you to associate dependent promises to an asynchronous action's eventual success value or
17-
failure reason. Promises are a way to write async code that still appears as though it is executing
18-
in a synchronous way.
20+
The Promise design pattern is used to handle asynchronous operations by providing a placeholder for a result that is initially unknown but will be resolved in the future.
1921

2022
## Explanation
2123

22-
The Promise object is used for asynchronous computations. A Promise represents an operation that
23-
hasn't completed yet, but is expected in the future.
24+
Real-world example
2425

25-
Promises provide a few advantages over callback objects:
26-
* Functional composition and error handling.
27-
* Prevents callback hell and provides callback aggregation.
28-
29-
Real world example
30-
31-
> We are developing a software solution that downloads files and calculates the number of lines and
32-
> character frequencies in those files. Promise is an ideal solution to make the code concise and
33-
> easy to understand.
26+
> In an online pizza ordering system, when a customer places an order, the system immediately acknowledges the order and provides a tracking number (the promise). The pizza preparation and delivery process happens asynchronously in the background. The customer can check the status of their order at any time using the tracking number. Once the pizza is prepared and out for delivery, the customer receives a notification (promise resolved) about the delivery status. If there are any issues, such as an unavailable ingredient or delivery delay, the customer is notified about the error (promise rejected).
27+
>
28+
> This analogy illustrates how the Promise design pattern manages asynchronous tasks, decoupling the initial request from the eventual outcome, and handling both results and errors efficiently.
3429
3530
In plain words
3631

3732
> Promise is a placeholder for an asynchronous operation that is ongoing.
3833
3934
Wikipedia says
4035

41-
> In computer science, future, promise, delay, and deferred refer to constructs used for
42-
> synchronizing program execution in some concurrent programming languages. They describe an object
43-
> that acts as a proxy for a result that is initially unknown, usually because the computation of
44-
> its value is not yet complete.
36+
> In computer science, future, promise, delay, and deferred refer to constructs used for synchronizing program execution in some concurrent programming languages. They describe an object that acts as a proxy for a result that is initially unknown, usually because the computation of its value is not yet complete.
4537
4638
**Programmatic Example**
4739

48-
In the example a file is downloaded and its line count is calculated. The calculated line count is
49-
then consumed and printed on console.
40+
The Promise design pattern is a software design pattern that's often used in concurrent programming to handle asynchronous operations. It represents a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason.
5041

51-
Let's first introduce a support class we need for implementation. Here's `PromiseSupport`.
42+
In the provided code, the Promise design pattern is used to handle various asynchronous operations such as downloading a file, counting lines in a file, and calculating the character frequency in a file.
5243

5344
```java
54-
class PromiseSupport<T> implements Future<T> {
55-
56-
private static final Logger LOGGER = LoggerFactory.getLogger(PromiseSupport.class);
57-
58-
private static final int RUNNING = 1;
59-
private static final int FAILED = 2;
60-
private static final int COMPLETED = 3;
61-
62-
private final Object lock;
63-
64-
private volatile int state = RUNNING;
65-
private T value;
66-
private Exception exception;
67-
68-
PromiseSupport() {
69-
this.lock = new Object();
70-
}
71-
72-
void fulfill(T value) {
73-
this.value = value;
74-
this.state = COMPLETED;
75-
synchronized (lock) {
76-
lock.notifyAll();
77-
}
78-
}
45+
@Slf4j
46+
public class App {
7947

80-
void fulfillExceptionally(Exception exception) {
81-
this.exception = exception;
82-
this.state = FAILED;
83-
synchronized (lock) {
84-
lock.notifyAll();
85-
}
86-
}
48+
private static final String DEFAULT_URL =
49+
"https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/promise/README.md";
50+
private final ExecutorService executor;
8751

88-
@Override
89-
public boolean cancel(boolean mayInterruptIfRunning) {
90-
return false;
52+
private App() {
53+
// Create a thread pool with 2 threads
54+
executor = Executors.newFixedThreadPool(2);
9155
}
9256

93-
@Override
94-
public boolean isCancelled() {
95-
return false;
57+
public static void main(String[] args) {
58+
var app = new App();
59+
app.promiseUsage();
9660
}
9761

98-
@Override
99-
public boolean isDone() {
100-
return state > RUNNING;
62+
private void promiseUsage() {
63+
calculateLineCount();
64+
calculateLowestFrequencyChar();
10165
}
10266

103-
@Override
104-
public T get() throws InterruptedException, ExecutionException {
105-
synchronized (lock) {
106-
while (state == RUNNING) {
107-
lock.wait();
108-
}
109-
}
110-
if (state == COMPLETED) {
111-
return value;
112-
}
113-
throw new ExecutionException(exception);
114-
}
115-
116-
@Override
117-
public T get(long timeout, TimeUnit unit) throws ExecutionException {
118-
synchronized (lock) {
119-
while (state == RUNNING) {
120-
try {
121-
lock.wait(unit.toMillis(timeout));
122-
} catch (InterruptedException e) {
123-
LOGGER.warn("Interrupted!", e);
124-
Thread.currentThread().interrupt();
67+
private void calculateLowestFrequencyChar() {
68+
// Create a promise to calculate the lowest frequency character
69+
lowestFrequencyChar().thenAccept(
70+
charFrequency -> {
71+
LOGGER.info("Char with lowest frequency is: {}", charFrequency);
12572
}
126-
}
127-
}
128-
129-
if (state == COMPLETED) {
130-
return value;
131-
}
132-
throw new ExecutionException(exception);
73+
);
13374
}
134-
}
135-
```
13675

137-
With `PromiseSupport` in place we can implement the actual `Promise`.
138-
139-
```java
140-
public class Promise<T> extends PromiseSupport<T> {
141-
142-
private Runnable fulfillmentAction;
143-
private Consumer<? super Throwable> exceptionHandler;
144-
145-
public Promise() {
146-
}
147-
148-
@Override
149-
public void fulfill(T value) {
150-
super.fulfill(value);
151-
postFulfillment();
152-
}
153-
154-
@Override
155-
public void fulfillExceptionally(Exception exception) {
156-
super.fulfillExceptionally(exception);
157-
handleException(exception);
158-
postFulfillment();
159-
}
160-
161-
private void handleException(Exception exception) {
162-
if (exceptionHandler == null) {
163-
return;
164-
}
165-
exceptionHandler.accept(exception);
166-
}
167-
168-
private void postFulfillment() {
169-
if (fulfillmentAction == null) {
170-
return;
171-
}
172-
fulfillmentAction.run();
173-
}
174-
175-
public Promise<T> fulfillInAsync(final Callable<T> task, Executor executor) {
176-
executor.execute(() -> {
177-
try {
178-
fulfill(task.call());
179-
} catch (Exception ex) {
180-
fulfillExceptionally(ex);
181-
}
182-
});
183-
return this;
184-
}
185-
186-
public Promise<Void> thenAccept(Consumer<? super T> action) {
187-
var dest = new Promise<Void>();
188-
fulfillmentAction = new ConsumeAction(this, dest, action);
189-
return dest;
190-
}
191-
192-
public Promise<T> onError(Consumer<? super Throwable> exceptionHandler) {
193-
this.exceptionHandler = exceptionHandler;
194-
return this;
195-
}
196-
197-
public <V> Promise<V> thenApply(Function<? super T, V> func) {
198-
Promise<V> dest = new Promise<>();
199-
fulfillmentAction = new TransformAction<>(this, dest, func);
200-
return dest;
76+
private void calculateLineCount() {
77+
// Create a promise to calculate the line count
78+
countLines().thenAccept(
79+
count -> {
80+
LOGGER.info("Line count is: {}", count);
81+
}
82+
);
20183
}
20284

203-
private class ConsumeAction implements Runnable {
204-
205-
private final Promise<T> src;
206-
private final Promise<Void> dest;
207-
private final Consumer<? super T> action;
208-
209-
private ConsumeAction(Promise<T> src, Promise<Void> dest, Consumer<? super T> action) {
210-
this.src = src;
211-
this.dest = dest;
212-
this.action = action;
213-
}
214-
215-
@Override
216-
public void run() {
217-
try {
218-
action.accept(src.get());
219-
dest.fulfill(null);
220-
} catch (Throwable throwable) {
221-
dest.fulfillExceptionally((Exception) throwable.getCause());
222-
}
223-
}
85+
private Promise<Character> lowestFrequencyChar() {
86+
// Create a promise to calculate the character frequency and then find the lowest frequency character
87+
return characterFrequency().thenApply(Utility::lowestFrequencyChar);
22488
}
22589

226-
private class TransformAction<V> implements Runnable {
227-
228-
private final Promise<T> src;
229-
private final Promise<V> dest;
230-
private final Function<? super T, V> func;
231-
232-
private TransformAction(Promise<T> src, Promise<V> dest, Function<? super T, V> func) {
233-
this.src = src;
234-
this.dest = dest;
235-
this.func = func;
236-
}
237-
238-
@Override
239-
public void run() {
240-
try {
241-
dest.fulfill(func.apply(src.get()));
242-
} catch (Throwable throwable) {
243-
dest.fulfillExceptionally((Exception) throwable.getCause());
244-
}
245-
}
90+
private Promise<Map<Character, Long>> characterFrequency() {
91+
// Create a promise to download a file and then calculate the character frequency
92+
return download(DEFAULT_URL).thenApply(Utility::characterFrequency);
24693
}
247-
}
248-
```
249-
250-
Now we can show the full example in action. Here's how to download and count the number of lines in
251-
a file using `Promise`.
252-
253-
```java
254-
countLines().thenAccept(
255-
count -> {
256-
LOGGER.info("Line count is: {}", count);
257-
taskCompleted();
258-
}
259-
);
26094

26195
private Promise<Integer> countLines() {
96+
// Create a promise to download a file and then count the lines
26297
return download(DEFAULT_URL).thenApply(Utility::countLines);
26398
}
26499

265100
private Promise<String> download(String urlString) {
101+
// Create a promise to download a file
266102
return new Promise<String>()
267103
.fulfillInAsync(
268104
() -> Utility.downloadFile(urlString), executor)
269105
.onError(
270106
throwable -> {
271-
throwable.printStackTrace();
272-
taskCompleted();
107+
LOGGER.error("An error occurred: ", throwable);
273108
}
274109
);
275110
}
111+
}
276112
```
277113

114+
In this code, the `Promise` class is used to create promises for various operations. The `thenApply` method is used to chain promises, meaning that the result of one promise is used as the input for the next promise. The `thenAccept` method is used to handle the result of a promise. The `fulfillInAsync` method is used to fulfill a promise asynchronously, and the `onError` method is used to handle any errors that occur while fulfilling the promise.
115+
278116
## Class diagram
279117

280-
![alt text](./etc/promise.png "Promise")
118+
![Promise](./etc/promise.png "Promise")
281119

282120
## Applicability
283121

284-
Promise pattern is applicable in concurrent programming when some work needs to be done
285-
asynchronously and:
122+
* When you need to perform asynchronous tasks and handle their results or errors at a later point.
123+
* In scenarios where tasks can be executed in parallel and their outcomes need to be handled once they are completed.
124+
* Suitable for improving the readability and maintainability of asynchronous code.
286125

287-
* Code maintainability and readability suffers due to callback hell.
288-
* You need to compose promises and need better error handling for asynchronous tasks.
289-
* You want to use functional style of programming.
126+
## Tutorials
290127

128+
* [Guide To CompletableFuture](https://www.baeldung.com/java-completablefuture)
291129

292-
## Real world examples
130+
## Known Uses
293131

294-
* [java.util.concurrent.CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html)
132+
* Java's CompletableFuture and Future classes.
133+
* JavaScript’s Promise object for managing asynchronous operations.
134+
* Many asynchronous frameworks and libraries such as RxJava and Vert.x.
295135
* [Guava ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained)
296136

297-
## Related Patterns
137+
## Consequences
298138

299-
* [Async Method Invocation](https://java-design-patterns.com/patterns/async-method-invocation/)
300-
* [Callback](https://java-design-patterns.com/patterns/callback/)
139+
Benefits:
301140

302-
## Tutorials
141+
* Improved Readability: Simplifies complex asynchronous code, making it easier to understand and maintain.
142+
* Decoupling: Decouples the code that initiates the asynchronous operation from the code that processes the result.
143+
* Error Handling: Provides a unified way to handle both results and errors from asynchronous operations.
303144

304-
* [Guide To CompletableFuture](https://www.baeldung.com/java-completablefuture)
145+
Trade-offs:
146+
147+
* Complexity: Can add complexity to the codebase if overused or misused.
148+
* Debugging: Asynchronous code can be harder to debug compared to synchronous code due to the non-linear flow of execution.
149+
150+
## Related Patterns
151+
152+
* [Observer](https://java-design-patterns.com/patterns/observer/): Promises can be used in conjunction with the Observer pattern to notify subscribers about the completion of asynchronous operations.
153+
* [Callback](https://java-design-patterns.com/patterns/callback/): Promises often replace callback mechanisms by providing a more structured and readable way to handle asynchronous results.
154+
* [Async Method Invocation](https://java-design-patterns.com/patterns/async-method-invocation/): Promises are often used to handle the results of asynchronous method invocations, allowing for non-blocking execution and result handling.
305155

306156
## Credits
307157

308158
* [You are missing the point to Promises](https://gist.github.com/domenic/3889970)
309159
* [Functional style callbacks using CompletableFuture](https://www.infoq.com/articles/Functional-Style-Callbacks-Using-CompletableFuture)
310160
* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://www.amazon.com/gp/product/1617291994/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617291994&linkId=995af46887bb7b65e6c788a23eaf7146)
311161
* [Modern Java in Action: Lambdas, streams, functional and reactive programming](https://www.amazon.com/gp/product/1617293563/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617293563&linkId=f70fe0d3e1efaff89554a6479c53759c)
162+
* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
163+
* [Effective Java](https://amzn.to/4cGk2Jz)
164+
* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://amzn.to/3QCmGXs)

0 commit comments

Comments
 (0)