1
1
# Promises Framework
2
2
3
- Promises is a new framework unifying former ` Concurrent::Future ` , ` Concurrent::Promise ` , ` Concurrent::IVar ` ,
4
- ` Concurrent::Event ` , ` Concurrent.dataflow ` , ` Delay ` , and ` TimerTask ` . It extensively uses the new
5
- synchronization layer to make all the features ** non-blocking** and
6
- ** lock-free** , with the exception of obviously blocking operations like
7
- ` #wait ` , ` #value ` . As a result it lowers a danger of deadlocking and offers
3
+ Promises is a new framework unifying former ` Concurrent::Future ` ,
4
+ ` Concurrent::Promise ` , ` Concurrent::IVar ` , ` Concurrent::Event ` ,
5
+ ` Concurrent.dataflow ` , ` Delay ` , and ` TimerTask ` . It extensively uses the new
6
+ synchronization layer to make all the features * non-blocking* and
7
+ * lock-free* , with the exception of obviously blocking operations like
8
+ ` #wait ` , ` #value ` , etc. As a result it lowers a danger of deadlocking and offers
8
9
better performance.
9
10
10
11
## Overview
11
12
12
- There are two central classes ... TODO
13
+ * TODO*
13
14
14
- ## Where does it executes?
15
-
16
- - TODO Explain ` _on ` ` _using ` sufixes.
15
+ - What is it?
16
+ - What is it for?
17
+ - Main classes {Future}, {Event}
18
+ - Explain ` _on ` ` _using ` suffixes.
17
19
18
20
## Old examples follow
19
21
20
22
* TODO rewrite into md with examples*
21
23
22
- Adds factory methods like: future, event, delay, schedule, zip, etc. Otherwise
23
- they can be called on Promises module .
24
+ Constructors are not accessible, instead there are many constructor methods in
25
+ FactoryMethods .
24
26
25
27
``` ruby
26
28
Concurrent ::Promises ::FactoryMethods .instance_methods false
29
+ ```
30
+
31
+ The module can be included or extended where needed.
32
+
33
+ ``` ruby
34
+ Class .new do
35
+ include Concurrent ::Promises ::FactoryMethods
36
+
37
+ def a_method
38
+ resolvable_event
39
+ end
40
+ end .new .a_method
27
41
42
+ Module .new { extend Concurrent ::Promises ::FactoryMethods }.resolvable_event
43
+ ```
44
+ The module is already extended into {Promises} for convenience.
45
+
46
+ ``` ruby
47
+ Concurrent ::Promises .resolvable_event
48
+ ```
49
+
50
+ For this guide we include the module into ` main ` so we can call the factory
51
+ methods in following examples directly.
52
+
53
+ ``` ruby
28
54
include Concurrent ::Promises ::FactoryMethods #
55
+ resolvable_event
29
56
```
30
57
31
58
Simple asynchronous task:
32
59
33
60
``` ruby
34
61
future = future(0.1 ) { |duration | sleep duration; :result } # evaluation starts immediately
35
- future.completed ?
62
+ future.resolved ?
36
63
# block until evaluated
37
64
future.value
38
- future.completed ?
65
+ future.resolved ?
39
66
```
40
67
41
- Failing asynchronous task
68
+ Rejecting asynchronous task
42
69
43
70
``` ruby
44
71
future = future { raise ' Boom' }
@@ -49,15 +76,15 @@ future.reason
49
76
raise future rescue $!
50
77
```
51
78
52
- Direct creation of completed futures
79
+ Direct creation of resolved futures
53
80
54
81
``` ruby
55
- succeeded_future (Object .new )
56
- failed_future (StandardError .new (" boom" ))
82
+ fulfilled_future (Object .new )
83
+ rejected_future (StandardError .new (" boom" ))
57
84
58
85
# ## Chaining of futures
59
86
60
- head = succeeded_future 1 #
87
+ head = fulfilled_future 1 #
61
88
branch1 = head.then(& :succ ) #
62
89
branch2 = head.then(& :succ ).then(& :succ ) #
63
90
branch1.zip(branch2).value!
@@ -66,7 +93,7 @@ branch1.zip(branch2).value!
66
93
(branch1 & branch2).then(& :+ ).value!
67
94
# or a class method zip from FactoryMethods can be used to zip multiple futures
68
95
zip(branch1, branch2, branch1).then { |* values | values.reduce & :+ }.value!
69
- # pick only first completed
96
+ # pick only first resolved
70
97
any(branch1, branch2).value!
71
98
(branch1 | branch2).value!
72
99
@@ -76,29 +103,29 @@ any(branch1, branch2).value!
76
103
# any supplied arguments are passed to the block, promises ensure that they are visible to the block
77
104
78
105
future(' 3' ) { |s | s.to_i }.then(2 ) { |a , b | a + b }.value
79
- succeeded_future (1 ).then(2 , & :+ ).value
80
- succeeded_future (1 ).chain(2 ) { |success , value , reason , arg | value + arg }.value
106
+ fulfilled_future (1 ).then(2 , & :+ ).value
107
+ fulfilled_future (1 ).chain(2 ) { |fulfilled , value , reason , arg | value + arg }.value
81
108
82
109
83
110
# ## Error handling
84
111
85
- succeeded_future (Object .new ).then(& :succ ).then(& :succ ).rescue { |e | e.class }.value # error propagates
86
- succeeded_future (Object .new ).then(& :succ ).rescue { 1 }.then(& :succ ).value # rescued and replaced with 1
87
- succeeded_future (1 ).then(& :succ ).rescue { |e | e.message }.then(& :succ ).value # no error, rescue not applied
112
+ fulfilled_future (Object .new ).then(& :succ ).then(& :succ ).rescue { |e | e.class }.value # error propagates
113
+ fulfilled_future (Object .new ).then(& :succ ).rescue { 1 }.then(& :succ ).value # rescued and replaced with 1
114
+ fulfilled_future (1 ).then(& :succ ).rescue { |e | e.message }.then(& :succ ).value # no error, rescue not applied
88
115
89
- failing_zip = succeeded_future (1 ) & failed_future (StandardError .new (' boom' ))
90
- failing_zip .result
91
- failing_zip .then { |v | ' never happens' }.result
92
- failing_zip .rescue { |a , b | (a || b).message }.value
93
- failing_zip .chain { |success , values , reasons | [success , values.compact, reasons.compactß ] }.value
116
+ rejected_zip = fulfilled_future (1 ) & rejected_future (StandardError .new (' boom' ))
117
+ rejected_zip .result
118
+ rejected_zip .then { |v | ' never happens' }.result
119
+ rejected_zip .rescue { |a , b | (a || b).message }.value
120
+ rejected_zip .chain { |fulfilled , values , reasons | [fulfilled , values.compact, reasons.compact ] }.value
94
121
95
122
96
123
# ## Delay
97
124
98
- # will not evaluate until asked by #value or other method requiring completion
125
+ # will not evaluate until asked by #value or other method requiring resolution
99
126
future = delay { ' lazy' }
100
127
sleep 0.1 #
101
- future.completed ?
128
+ future.resolved ?
102
129
future.value
103
130
104
131
# propagates trough chain allowing whole or partial lazy chains
@@ -108,15 +135,15 @@ branch1 = head.then(&:succ)
108
135
branch2 = head.delay.then(& :succ )
109
136
join = branch1 & branch2
110
137
111
- sleep 0.1 # nothing will complete
112
- [head, branch1, branch2, join].map(& :completed ? )
138
+ sleep 0.1 # nothing will resolve
139
+ [head, branch1, branch2, join].map(& :resolved ? )
113
140
114
141
branch1.value
115
- sleep 0.1 # forces only head to complete , branch 2 stays incomplete
116
- [head, branch1, branch2, join].map(& :completed ? )
142
+ sleep 0.1 # forces only head to resolve , branch 2 stays pending
143
+ [head, branch1, branch2, join].map(& :resolved ? )
117
144
118
145
join.value
119
- [head, branch1, branch2, join].map(& :completed ? )
146
+ [head, branch1, branch2, join].map(& :resolved ? )
120
147
121
148
122
149
# ## Flatting
@@ -136,7 +163,7 @@ future { future { future { 1 + 1 } } }.
136
163
# it'll be executed after 0.1 seconds
137
164
scheduled = schedule(0.1 ) { 1 }
138
165
139
- scheduled.completed ?
166
+ scheduled.resolved ?
140
167
scheduled.value # available after 0.1sec
141
168
142
169
# and in chain
@@ -146,19 +173,19 @@ sleep 0.1 #
146
173
scheduled.value # returns after another 0.1sec
147
174
148
175
149
- # ## Completable Future and Event
176
+ # ## Resolvable Future and Event
150
177
151
- future = completable_future
152
- event = completable_event ()
178
+ future = resolvable_future
179
+ event = resolvable_event ()
153
180
154
- # These threads will be blocked until the future and event is completed
181
+ # These threads will be blocked until the future and event is resolved
155
182
t1 = Thread .new { future.value } #
156
183
t2 = Thread .new { event.wait } #
157
184
158
- future.success 1
159
- future.success 1 rescue $!
160
- future.success 2 , false
161
- event.complete
185
+ future.fulfill 1
186
+ future.fulfill 1 rescue $!
187
+ future.fulfill 2 , false
188
+ event.resolve
162
189
163
190
# The threads can be joined now
164
191
[t1, t2].each & :join #
@@ -169,8 +196,8 @@ event.complete
169
196
queue = Queue .new
170
197
future = delay { 1 + 1 }
171
198
172
- future.on_success { queue << 1 } # evaluated asynchronously
173
- future.on_success ! { queue << 2 } # evaluated on completing thread
199
+ future.on_fulfillment { queue << 1 } # evaluated asynchronously
200
+ future.on_fulfillment ! { queue << 2 } # evaluated on resolving thread
174
201
175
202
queue.empty?
176
203
future.value
@@ -236,8 +263,8 @@ zip(*jobs).value
236
263
# periodic task
237
264
def schedule_job (interval , & job )
238
265
# schedule the first execution and chain restart og the job
239
- Concurrent .schedule(interval, & job).chain do |success , continue , reason |
240
- if success
266
+ Concurrent .schedule(interval, & job).chain do |fulfilled , continue , reason |
267
+ if fulfilled
241
268
schedule_job(interval, & job) if continue
242
269
else
243
270
# handle error
@@ -281,10 +308,10 @@ end
281
308
282
309
concurrent_jobs = 11 .times.map do |v |
283
310
284
- succeeded_future (v).
311
+ fulfilled_future (v).
285
312
# ask the DB with the `v`, only one at the time, rest is parallel
286
313
then_ask(DB ).
287
- # get size of the string, fails for 11
314
+ # get size of the string, rejects for 11
288
315
then (& :size ).
289
316
rescue { |reason | reason.message } # translate error to value (exception, message)
290
317
end #
308
335
309
336
concurrent_jobs = 11 .times.map do |v |
310
337
311
- succeeded_future (v).
338
+ fulfilled_future (v).
312
339
# ask the DB_POOL with the `v`, only 5 at the time, rest is parallel
313
340
then_ask(DB_POOL ).
314
341
then (& :size ).
0 commit comments