9
9
---
10
10
11
11
## Also known as
12
+
12
13
CompletableFuture
13
14
14
15
## Intent
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.
16
+
17
+ A Promise represents a proxy for a value not necessarily known when the promise is created. It allows you to associate
18
+ dependent promises to an asynchronous action's eventual success value or failure reason. Promises are a way to write
19
+ async code that still appears as though it is executing in a synchronous way.
20
+
21
+ ## Explanation
22
+
23
+ The Promise object is used for asynchronous computations. A Promise represents an operation that hasn't completed yet,
24
+ but is expected in the future.
25
+
26
+ Promises provide a few advantages over callback objects:
27
+ * Functional composition and error handling
28
+ * Prevents callback hell and provides callback aggregation
29
+
30
+ Real world example
31
+
32
+ > We are developing a software solution that downloads files and calculates the number of lines and character
33
+ frequencies in those files. Promise is an ideal solution to make the code concise and easy to understand.
34
+
35
+ In plain words
36
+
37
+ > Promise is a placeholder for an asynchronous operation that is ongoing.
38
+
39
+ Wikipedia says
40
+
41
+ > In computer science, future, promise, delay, and deferred refer to constructs used for synchronizing program
42
+ execution in some concurrent programming languages. They describe an object that acts as a proxy for a result that is
43
+ initially unknown, usually because the computation of its value is not yet complete.
44
+
45
+ ** Programmatic Example**
46
+
47
+ In the example a file is downloaded and its line count is calculated. The calculated line count is then consumed and
48
+ printed on console.
49
+
50
+ Let's first introduce a support class we need for implementation. Here's ` PromiseSupport ` .
51
+
52
+ ``` java
53
+ class PromiseSupport <T> implements Future<T > {
54
+
55
+ private static final Logger LOGGER = LoggerFactory . getLogger(PromiseSupport . class);
56
+
57
+ private static final int RUNNING = 1 ;
58
+ private static final int FAILED = 2 ;
59
+ private static final int COMPLETED = 3 ;
60
+
61
+ private final Object lock;
62
+
63
+ private volatile int state = RUNNING ;
64
+ private T value;
65
+ private Exception exception;
66
+
67
+ PromiseSupport () {
68
+ this . lock = new Object ();
69
+ }
70
+
71
+ void fulfill (T value ) {
72
+ this . value = value;
73
+ this . state = COMPLETED ;
74
+ synchronized (lock) {
75
+ lock. notifyAll();
76
+ }
77
+ }
78
+
79
+ void fulfillExceptionally (Exception exception ) {
80
+ this . exception = exception;
81
+ this . state = FAILED ;
82
+ synchronized (lock) {
83
+ lock. notifyAll();
84
+ }
85
+ }
86
+
87
+ @Override
88
+ public boolean cancel (boolean mayInterruptIfRunning ) {
89
+ return false ;
90
+ }
91
+
92
+ @Override
93
+ public boolean isCancelled () {
94
+ return false ;
95
+ }
96
+
97
+ @Override
98
+ public boolean isDone () {
99
+ return state > RUNNING ;
100
+ }
101
+
102
+ @Override
103
+ public T get () throws InterruptedException , ExecutionException {
104
+ synchronized (lock) {
105
+ while (state == RUNNING ) {
106
+ lock. wait();
107
+ }
108
+ }
109
+ if (state == COMPLETED ) {
110
+ return value;
111
+ }
112
+ throw new ExecutionException (exception);
113
+ }
114
+
115
+ @Override
116
+ public T get (long timeout , TimeUnit unit ) throws ExecutionException {
117
+ synchronized (lock) {
118
+ while (state == RUNNING ) {
119
+ try {
120
+ lock. wait(unit. toMillis(timeout));
121
+ } catch (InterruptedException e) {
122
+ LOGGER . warn(" Interrupted!" , e);
123
+ Thread . currentThread(). interrupt();
124
+ }
125
+ }
126
+ }
127
+
128
+ if (state == COMPLETED ) {
129
+ return value;
130
+ }
131
+ throw new ExecutionException (exception);
132
+ }
133
+ }
134
+ ```
135
+
136
+ With ` PromiseSupport ` in place we can implement the actual ` Promise ` .
137
+
138
+ ``` java
139
+ public class Promise <T> extends PromiseSupport<T > {
140
+
141
+ private Runnable fulfillmentAction;
142
+ private Consumer<? super Throwable > exceptionHandler;
143
+
144
+ public Promise () {
145
+ }
146
+
147
+ @Override
148
+ public void fulfill (T value ) {
149
+ super . fulfill(value);
150
+ postFulfillment();
151
+ }
152
+
153
+ @Override
154
+ public void fulfillExceptionally (Exception exception ) {
155
+ super . fulfillExceptionally(exception);
156
+ handleException(exception);
157
+ postFulfillment();
158
+ }
159
+
160
+ private void handleException (Exception exception ) {
161
+ if (exceptionHandler == null ) {
162
+ return ;
163
+ }
164
+ exceptionHandler. accept(exception);
165
+ }
166
+
167
+ private void postFulfillment () {
168
+ if (fulfillmentAction == null ) {
169
+ return ;
170
+ }
171
+ fulfillmentAction. run();
172
+ }
173
+
174
+ public Promise<T > fulfillInAsync (final Callable<T > task , Executor executor ) {
175
+ executor. execute(() - > {
176
+ try {
177
+ fulfill(task. call());
178
+ } catch (Exception ex) {
179
+ fulfillExceptionally(ex);
180
+ }
181
+ });
182
+ return this ;
183
+ }
184
+
185
+ public Promise<Void > thenAccept (Consumer<? super T > action ) {
186
+ var dest = new Promise<Void > ();
187
+ fulfillmentAction = new ConsumeAction (this , dest, action);
188
+ return dest;
189
+ }
190
+
191
+ public Promise<T > onError (Consumer<? super Throwable > exceptionHandler ) {
192
+ this . exceptionHandler = exceptionHandler;
193
+ return this ;
194
+ }
195
+
196
+ public <V > Promise<V > thenApply (Function<? super T , V > func ) {
197
+ Promise<V > dest = new Promise<> ();
198
+ fulfillmentAction = new TransformAction<V > (this , dest, func);
199
+ return dest;
200
+ }
201
+
202
+ private class ConsumeAction implements Runnable {
203
+
204
+ private final Promise<T > src;
205
+ private final Promise<Void > dest;
206
+ private final Consumer<? super T > action;
207
+
208
+ private ConsumeAction (Promise<T > src , Promise<Void > dest , Consumer<? super T > action ) {
209
+ this . src = src;
210
+ this . dest = dest;
211
+ this . action = action;
212
+ }
213
+
214
+ @Override
215
+ public void run () {
216
+ try {
217
+ action. accept(src. get());
218
+ dest. fulfill(null );
219
+ } catch (Throwable throwable) {
220
+ dest. fulfillExceptionally((Exception ) throwable. getCause());
221
+ }
222
+ }
223
+ }
224
+
225
+ private class TransformAction <V> implements Runnable {
226
+
227
+ private final Promise<T > src;
228
+ private final Promise<V > dest;
229
+ private final Function<? super T , V > func;
230
+
231
+ private TransformAction (Promise<T > src , Promise<V > dest , Function<? super T , V > func ) {
232
+ this . src = src;
233
+ this . dest = dest;
234
+ this . func = func;
235
+ }
236
+
237
+ @Override
238
+ public void run () {
239
+ try {
240
+ dest. fulfill(func. apply(src. get()));
241
+ } catch (Throwable throwable) {
242
+ dest. fulfillExceptionally((Exception ) throwable. getCause());
243
+ }
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ Now we can show the full example in action. Here's how to download and count the number of lines in a file using
250
+ ` Promise ` .
251
+
252
+ ``` java
253
+ countLines(). thenAccept(
254
+ count - > {
255
+ LOGGER . info(" Line count is: {}" , count);
256
+ taskCompleted();
257
+ }
258
+ );
259
+
260
+ private Promise<Integer > countLines() {
261
+ return download(DEFAULT_URL ). thenApply(Utility :: countLines);
262
+ }
263
+
264
+ private Promise<String > download(String urlString) {
265
+ return new Promise<String > ()
266
+ .fulfillInAsync(
267
+ () - > Utility . downloadFile(urlString), executor)
268
+ .onError(
269
+ throwable - > {
270
+ throwable. printStackTrace();
271
+ taskCompleted();
272
+ }
273
+ );
274
+ }
275
+ ```
19
276
20
277
## Class diagram
278
+
21
279
![ alt text] ( ./etc/promise.png " Promise ")
22
280
23
281
## Applicability
282
+
24
283
Promise pattern is applicable in concurrent programming when some work needs to be done asynchronously
25
284
and:
26
285
@@ -35,10 +294,17 @@ and:
35
294
* [ Guava ListenableFuture] ( https://github.com/google/guava/wiki/ListenableFutureExplained )
36
295
37
296
## Related Patterns
38
- * Async Method Invocation
39
- * Callback
297
+
298
+ * [ Async Method Invocation] ( https://java-design-patterns.com/patterns/async-method-invocation/ )
299
+ * [ Callback] ( https://java-design-patterns.com/patterns/callback/ )
300
+
301
+ ## Tutorials
302
+
303
+ * [ Guide To CompletableFuture] ( https://www.baeldung.com/java-completablefuture )
40
304
41
305
## Credits
42
306
43
307
* [ You are missing the point to Promises] ( https://gist.github.com/domenic/3889970 )
44
308
* [ Functional style callbacks using CompletableFuture] ( https://www.infoq.com/articles/Functional-Style-Callbacks-Using-CompletableFuture )
309
+ * [ 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 )
310
+ * [ 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 )
0 commit comments