You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: lib/concurrent/promise.rb
+80-44Lines changed: 80 additions & 44 deletions
Original file line number
Diff line number
Diff line change
@@ -7,15 +7,41 @@ module Concurrent
7
7
8
8
PromiseExecutionError=Class.new(StandardError)
9
9
10
-
# Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A) and [Promises/A+](http://promises-aplus.github.io/promises-spec/) specifications.
10
+
# Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A)
11
+
# and [Promises/A+](http://promises-aplus.github.io/promises-spec/) specifications.
11
12
#
12
13
# > A promise represents the eventual value returned from the single completion of an operation.
13
14
#
14
-
# Promises are similar to futures and share many of the same behaviours. Promises are far more robust, however. Promises can be chained in a tree structure where each promise may have zero or more children. Promises are chained using the `then` method. The result of a call to `then` is always another promise. Promises are resolved asynchronously (with respect to the main thread) but in a strict order: parents are guaranteed to be resolved before their children, children before their younger siblings. The `then` method takes two parameters: an optional block to be executed upon parent resolution and an optional callable to be executed upon parent failure. The result of each promise is passed to each of its children upon resolution. When a promise is rejected all its children will be summarily rejected and will receive the reason.
15
-
#
16
-
# Promises have four possible states: *unscheduled*, *pending*, *rejected*, and *fulfilled*. A Promise created using `.new` will be *unscheduled*. It is scheduled by calling the `execute` method. Upon execution the Promise and all its children will be set to *pending*. When a promise is *pending* it will remain in that state until processing is complete. A completed Promise is either *rejected*, indicating that an exception was thrown during processing, or *fulfilled*, indicating it succeeded. If a Promise is *fulfilled* its `value` will be updated to reflect the result of the operation. If *rejected* the `reason` will be updated with a reference to the thrown exception. The predicate methods `unscheduled?`, `pending?`, `rejected?`, and `fulfilled?` can be called at any time to obtain the state of the Promise, as can the `state` method, which returns a symbol. A Promise created using `.execute` will be *pending*, a Promise created using `.fulfill(value)` will be *fulfilled* with the given value and a Promise created using `.reject(reason)` will be *rejected* with the given reason.
17
-
#
18
-
# Retrieving the value of a promise is done through the `value` (alias: `deref`) method. Obtaining the value of a promise is a potentially blocking operation. When a promise is *rejected* a call to `value` will return `nil` immediately. When a promise is *fulfilled* a call to `value` will immediately return the current value. When a promise is *pending* a call to `value` will block until the promise is either *rejected* or *fulfilled*. A *timeout* value can be passed to `value` to limit how long the call will block. If `nil` the call will block indefinitely. If `0` the call will not block. Any other integer or float value will indicate the maximum number of seconds to block.
15
+
# Promises are similar to futures and share many of the same behaviours. Promises are far more
16
+
# robust, however. Promises can be chained in a tree structure where each promise may have zero
17
+
# or more children. Promises are chained using the `then` method. The result of a call to `then`
18
+
# is always another promise. Promises are resolved asynchronously (with respect to the main thread)
19
+
# but in a strict order: parents are guaranteed to be resolved before their children, children
20
+
# before their younger siblings. The `then` method takes two parameters: an optional block to
21
+
# be executed upon parent resolution and an optional callable to be executed upon parent failure.
22
+
# The result of each promise is passed to each of its children upon resolution. When a promise
23
+
# is rejected all its children will be summarily rejected and will receive the reason.
24
+
#
25
+
# Promises have four possible states: *unscheduled*, *pending*, *rejected*, and *fulfilled*. A
26
+
# Promise created using `.new` will be *unscheduled*. It is scheduled by calling the `execute`
27
+
# method. Upon execution the Promise and all its children will be set to *pending*. When a promise
28
+
# is *pending* it will remain in that state until processing is complete. A completed Promise is
29
+
# either *rejected*, indicating that an exception was thrown during processing, or *fulfilled*,
30
+
# indicating it succeeded. If a Promise is *fulfilled* its `value` will be updated to reflect
31
+
# the result of the operation. If *rejected* the `reason` will be updated with a reference to
32
+
# the thrown exception. The predicate methods `unscheduled?`, `pending?`, `rejected?`, and
33
+
# `fulfilled?` can be called at any time to obtain the state of the Promise, as can the `state`
34
+
# method, which returns a symbol. A Promise created using `.execute` will be *pending*, a Promise
35
+
# created using `.fulfill(value)` will be *fulfilled* with the given value and a Promise created
36
+
# using `.reject(reason)` will be *rejected* with the given reason.
37
+
#
38
+
# Retrieving the value of a promise is done through the `value` (alias: `deref`) method. Obtaining
39
+
# the value of a promise is a potentially blocking operation. When a promise is *rejected* a call
40
+
# to `value` will return `nil` immediately. When a promise is *fulfilled* a call to `value` will
41
+
# immediately return the current value. When a promise is *pending* a call to `value` will block
42
+
# until the promise is either *rejected* or *fulfilled*. A *timeout* value can be passed to `value`
43
+
# to limit how long the call will block. If `nil` the call will block indefinitely. If `0` the call
44
+
# will not block. Any other integer or float value will indicate the maximum number of seconds to block.
19
45
#
20
46
# Promises run on the global thread pool.
21
47
#
@@ -30,13 +56,15 @@ module Concurrent
30
56
# Then create one
31
57
#
32
58
# ```ruby
33
-
# p = Promise.execute do
59
+
# p = Concurrent::Promise.execute do
34
60
# # do something
35
61
# 42
36
62
# end
37
63
# ```
38
64
#
39
-
# Promises can be chained using the `then` method. The `then` method accepts a block, to be executed on fulfillment, and a callable argument to be executed on rejection. The result of the each promise is passed as the block argument to chained promises.
65
+
# Promises can be chained using the `then` method. The `then` method accepts a block, to be executed
66
+
# on fulfillment, and a callable argument to be executed on rejection. The result of the each promise
67
+
# is passed as the block argument to chained promises.
40
68
#
41
69
# ```ruby
42
70
# p = Concurrent::Promise.new{10}.then{|x| x * 2}.then{|result| result - 10 }.execute
@@ -57,7 +85,10 @@ module Concurrent
57
85
# - if parent is *fulfilled* the child will be *pending*
58
86
# - if parent is *rejected* the child will be *pending* (but will ultimately be *rejected*)
59
87
#
60
-
# Promises are executed asynchronously from the main thread. By the time a child Promise finishes initialization it may be in a different state that its parent (by the time a child is created its parent may have completed execution and changed state). Despite being asynchronous, however, the order of execution of Promise objects in a chain (or tree) is strictly defined.
88
+
# Promises are executed asynchronously from the main thread. By the time a child Promise finishes
89
+
# nitialization it may be in a different state that its parent (by the time a child is created its parent
90
+
# may have completed execution and changed state). Despite being asynchronous, however, the order of
91
+
# execution of Promise objects in a chain (or tree) is strictly defined.
61
92
#
62
93
# There are multiple ways to create and execute a new `Promise`. Both ways provide identical behavior:
63
94
#
@@ -107,7 +138,8 @@ module Concurrent
107
138
#
108
139
# #### Rejection
109
140
#
110
-
# When a promise is rejected all its children will be rejected and will receive the rejection `reason` as the rejection callable parameter:
141
+
# When a promise is rejected all its children will be rejected and will receive the rejection `reason`
142
+
# as the rejection callable parameter:
111
143
#
112
144
# ```ruby
113
145
# p = [ Concurrent::Promise.execute{ Thread.pass; raise StandardError } ]
@@ -121,11 +153,15 @@ module Concurrent
121
153
# c2.state #=> :rejected
122
154
# ```
123
155
#
124
-
# Once a promise is rejected it will continue to accept children that will receive immediately rejection (they will be executed asynchronously).
156
+
# Once a promise is rejected it will continue to accept children that will receive immediately rejection
157
+
# (they will be executed asynchronously).
125
158
#
126
159
# #### Aliases
127
160
#
128
-
# The `then` method is the most generic alias: it accepts a block to be executed upon parent fulfillment and a callable to be executed upon parent rejection. At least one of them should be passed. The default block is `{ |result| result }` that fulfills the child with the parent value. The default callable is `{ |reason| raise reason }` that rejects the child with the parent reason.
161
+
# The `then` method is the most generic alias: it accepts a block to be executed upon parent fulfillment
162
+
# and a callable to be executed upon parent rejection. At least one of them should be passed. The default
163
+
# block is `{ |result| result }` that fulfills the child with the parent value. The default callable is
164
+
# `{ |reason| raise reason }` that rejects the child with the parent reason.
129
165
#
130
166
# - `on_success { |result| ... }` is the same as `then {|result| ... }`
131
167
# - `rescue { |reason| ... }` is the same as `then(Proc.new { |reason| ... } )`
@@ -292,13 +328,11 @@ def zip(*others)
292
328
self.class.zip(self, *others)
293
329
end
294
330
295
-
# Aggregate a collection of zero or more promises under a composite promise,
296
-
# execute the aggregated promises and collect them into a standard Ruby array,
297
-
# call the given Ruby `Ennnumerable` predicate (such as `any?`, `all?`, `none?`,
298
-
# or `one?`) on the collection checking for the success or failure of each,
299
-
# then executing the composite's `#then` handlers if the predicate returns
300
-
# `true` or executing the composite's `#rescue` handlers if the predicate
301
-
# returns false.
331
+
# Aggregates a collection of promises and executes the `then` condition
332
+
# if all aggregated promises succeed. Executes the `rescue` handler with
333
+
# a `Concurrent::PromiseExecutionError` if any of the aggregated promises
334
+
# fail. Upon execution will execute any of the aggregate promises that
335
+
# were not already executed.
302
336
#
303
337
# @!macro [attach] promise_self_aggregate
304
338
#
@@ -314,28 +348,7 @@ def zip(*others)
314
348
# @param [Array] promises Zero or more promises to aggregate
315
349
# @return [Promise] an unscheduled (not executed) promise that aggregates
0 commit comments