1
1
# Promises, Promises...
2
2
3
- A promise is the most powerful and versatile of the concurrency objects in this library.
4
3
Promises are inspired by the JavaScript [ Promises/A] ( http://wiki.commonjs.org/wiki/Promises/A )
5
4
and [ Promises/A+] ( http://promises-aplus.github.io/promises-spec/ ) specifications.
6
5
@@ -9,23 +8,24 @@ and [Promises/A+](http://promises-aplus.github.io/promises-spec/) specifications
9
8
Promises are similar to futures and share many of the same behaviours. Promises are far more robust,
10
9
however. Promises can be chained in a tree structure where each promise may have zero or more children.
11
10
Promises are chained using the ` then ` method. The result of a call to ` then ` is always another promise.
12
- Promises are resolved asynchronously: parents are guaranteed to be resolved before their children.
13
- ` then ` takes two parameters: an optional block to be executed upon parent resolution and an optional callable
14
- to be executed upon parent failure.
15
- The result of each promise is passed to each of its children upon resolution.
16
- When a promise is rejected all its children will receive the reason.
11
+ Promises are resolved asynchronously (with respect to the main thread) but in a strict order:
12
+ parents are guaranteed to be resolved before their children, children before their younger siblings.
13
+ The ` then ` method takes two parameters: an optional block to be executed upon parent resolution and an
14
+ optional callable to be executed upon parent failure. The result of each promise is passed to each of its
15
+ children upon resolution. When a promise is rejected all its children will be summarily rejected and will
16
+ receive the reason.
17
17
18
18
Promises have four possible states: * unscheduled* , * pending* , * rejected* , and * fulfilled* .
19
- Promise created using ` .new ` will be * unscheduled* , to schedule it ` execute ` method must be called and the
20
- Promise with all its children will be set to * pending* .
21
- When a promise is * pending * will remain in that state until processing is complete.
22
- A completed promise is either * rejected * , indicating that an exception was thrown during processing, or * fulfilled* , indicating it succeeded.
23
- If a promise is * fulfilled* its ` value ` will be updated to reflect the result of the operation.
19
+ A Promise created using ` .new ` will be * unscheduled* . It is scheduled by calling the ` execute ` method.
20
+ Upon execution the Promise and all its children will be set to * pending* . When a promise is * pending * it will remain in that
21
+ state until processing is complete. A completed Promise is either * rejected * , indicating that an exception
22
+ was thrown during processing, or * fulfilled* , indicating it succeeded.
23
+ If a Promise is * fulfilled* its ` value ` will be updated to reflect the result of the operation.
24
24
If * rejected* the ` reason ` will be updated with a reference to the thrown exception.
25
- The predicate methods ` unscheduled? ` , ` pending? ` , ` rejected ` , and ` fulfilled? `
26
- can be called at any time to obtain the state of the promise , as can the ` state ` method, which returns a symbol.
27
- Promise created using ` .execute ` will be * pending* , a Promise created using ` .fulfill(value) ` will be * fulfilled*
28
- with the passed value and a Promise created using ` .reject(reason) ` will be * rejected* .
25
+ The predicate methods ` unscheduled? ` , ` pending? ` , ` rejected? ` , and ` fulfilled? `
26
+ can be called at any time to obtain the state of the Promise , as can the ` state ` method, which returns a symbol.
27
+ A Promise created using ` .execute ` will be * pending* , a Promise created using ` .fulfill(value) ` will be * fulfilled*
28
+ with the given value and a Promise created using ` .reject(reason) ` will be * rejected* with the given reason .
29
29
30
30
Retrieving the value of a promise is done through the ` value ` (alias: ` deref ` ) method. Obtaining the value of
31
31
a promise is a potentially blocking operation. When a promise is * rejected* a call to ` value ` will return ` nil `
@@ -59,7 +59,7 @@ accepts a block, to be executed on fulfillment, and a callable argument to be ex
59
59
The result of the each promise is passed as the block argument to chained promises.
60
60
61
61
``` ruby
62
- p = Concurrent ::Promise .new {10 }.then{|x | x * 2 }.then{|result | result - 10 }
62
+ p = Concurrent ::Promise .new {10 }.then{|x | x * 2 }.then{|result | result - 10 }.execute
63
63
```
64
64
65
65
And so on, and so on, and so on...
@@ -68,24 +68,44 @@ And so on, and so on, and so on...
68
68
p = Concurrent ::Promise .fulfill(20 ).
69
69
then {|result | result - 10 }.
70
70
then {|result | result * 3 }.
71
- then {|result | result % 5 }
71
+ then {|result | result % 5 }.execute
72
72
```
73
73
74
- Newly created promise state depends on the parent's one :
74
+ The initial state of a newly created Promise depends on the state of its parent :
75
75
- if parent is * unscheduled* the child will be * unscheduled*
76
76
- if parent is * pending* the child will be * pending*
77
77
- if parent is * fulfilled* the child will be * pending*
78
- - if parent is * rejected* the child will be * pending*
79
- Promises are executed asynchronously so a newly-created promise could be in a different state right after
80
- ` then ` returns, for example a child of a * rejected* parent could be already * fulfilled* or * rejected*
78
+ - if parent is * rejected* the child will be * pending* (but will ultimately be * rejected* )
79
+
80
+ Promises are executed asynchronously from the main thread. By the time a child Promise finishes initialization
81
+ it may be in a different state that its parent (by the time a child is created its parent may have completed
82
+ execution and changed state). Despite being asynchronous, however, the order of execution of Promise objects
83
+ in a chain (or tree) is strictly defined.
84
+
85
+ There are multiple ways to create and execute a new ` Promise ` . Both ways provide identical behavior:
86
+
87
+ ``` ruby
88
+ # create, operate, then execute
89
+ p1 = Concurrent ::Promise .new { " Hello World!" }
90
+ p1.state # => :unscheduled
91
+ p1.execute
92
+
93
+ # create and immediately execute
94
+ p2 = Concurrent ::Promise .new { " Hello World!" }.execute
95
+
96
+ # execute during creation
97
+ p3 = Concurrent ::Promise .execute{ " Hello World!" }
98
+ ```
99
+
100
+ Once the ` execute ` method is called a ` Promise ` becomes ` pending ` :
81
101
82
102
``` ruby
83
103
p = Concurrent ::Promise .execute{ " Hello, world!" }
84
- p .state # => :pending
104
+ p .state # => :pending
85
105
p .pending? # => true
86
106
```
87
107
88
- Wait a little bit, and the promise will resolve and provide a value
108
+ Wait a little bit, and the promise will resolve and provide a value:
89
109
90
110
``` ruby
91
111
p = Concurrent ::Promise .execute{ " Hello, world!" }
@@ -97,7 +117,7 @@ p.value #=> "Hello, world!"
97
117
```
98
118
99
119
If an exception occurs, the promise will be rejected and will provide
100
- a reason for the rejection
120
+ a reason for the rejection:
101
121
102
122
``` ruby
103
123
p = Concurrent ::Promise .execute{ raise StandardError .new (" Here comes the Boom!" ) }
@@ -110,30 +130,30 @@ p.reason #=> "#<StandardError: Here comes the Boom!>"
110
130
111
131
### Rejection
112
132
113
- When a promise is rejected all its children will be receive the reason as the rejection callable parameter.
133
+ When a promise is rejected all its children will be rejected and will receive the rejection ` reason ` as the
134
+ rejection callable parameter:
114
135
115
136
``` ruby
116
137
p = [ Concurrent ::Promise .execute{ Thread .pass; raise StandardError } ]
117
138
118
139
c1 = p .then(Proc .new { |reason | 42 })
119
140
c2 = p .then(Proc .new { |reason | raise ' Boom!' })
120
141
121
-
122
142
sleep (0.1 )
123
143
124
- c1.state # => :fulfilled
144
+ c1.state # => :rejected
125
145
c2.state # => :rejected
126
146
```
127
147
128
148
Once a promise is rejected it will continue to accept children that will receive immediately
129
- rejection (they will be executed asynchronously)
149
+ rejection (they will be executed asynchronously).
130
150
131
151
### Aliases
132
152
133
- ` then ` method is the most generic one : it accepts a block to be executed upon parent fulfillment
153
+ The ` then ` method is the most generic alias : it accepts a block to be executed upon parent fulfillment
134
154
and a callable to be executed upon parent rejection. At least one of them should be passed.
135
- Default block is ` { |result| result } ` that fulfills the child with the parent value.
136
- Default callable is ` { |reason| raise reason } ` that rejects the child with the parent reason.
155
+ The default block is ` { |result| result } ` that fulfills the child with the parent value.
156
+ The default callable is ` { |reason| raise reason } ` that rejects the child with the parent reason.
137
157
138
158
` on_success { |result| ... } ` is the same as ` then {|result| ... } `
139
159
` rescue { |reason| ... } ` is the same as ` then(Proc.new { |reason| ... } ) `
0 commit comments