Class: Promise
Overview
Promise is used to help structure asynchronous code.
It is available in the Opal standard library, and can be required in any Opal application:
require 'promise'
Basic Usage
Promises are created and returned as objects with the assumption that they will eventually be resolved or rejected, but never both. A Promise has a #then and #fail method (or one of their aliases) that can be used to register a block that gets called once resolved or rejected.
promise = Promise.new
promise.then {
puts "resolved!"
}.fail {
puts "rejected!"
}
# some time later
promise.resolve
# => "resolved!"
It is important to remember that a promise can only be resolved or rejected once, so the block will only ever be called once (or not at all).
Resolving Promises
To resolve a promise, means to inform the Promise that it has succeeded or evaluated to a useful value. #resolve can be passed a value which is then passed into the block handler:
def get_json
promise = Promise.new
HTTP.get("some_url") do |req|
promise.resolve req.json
end
promise
end
get_json.then do |json|
puts "got some JSON from server"
end
Rejecting Promises
Promises are also designed to handle error cases, or situations where an outcome is not as expected. Taking the previous example, we can also pass a value to a #reject call, which passes that object to the registered #fail handler:
def get_json
promise = Promise.new
HTTP.get("some_url") do |req|
if req.ok?
promise.resolve req.json
else
promise.reject req
end
promise
end
get_json.then {
# ...
}.fail { |req|
puts "it went wrong: #{req.message}"
}
Chaining Promises
Promises become even more useful when chained together. Each #then or #fail call returns a new Promise which can be used to chain more and more handlers together.
promise.then { wait_for_something }.then { do_something_else }
Rejections are propagated through the entire chain, so a "catch all" handler can be attached at the end of the tail:
promise.then { ... }.then { ... }.fail { ... }
Composing Promises
Promise.when can be used to wait for more than one promise to resolve (or reject). Using the previous example, we could request two different json requests and wait for both to finish:
Promise.when(get_json, get_json2).then |first, second|
puts "got two json payloads: #{first}, #{second}"
end
Defined Under Namespace
Instance Attribute Summary collapse
-
#error ⇒ Object
readonly
Returns the value of attribute error.
-
#next ⇒ Object
readonly
Returns the value of attribute next.
-
#prev ⇒ Object
readonly
Returns the value of attribute prev.
-
#value ⇒ Object
readonly
Returns the value of attribute value.
Class Method Summary collapse
Instance Method Summary collapse
- #<<(promise) ⇒ Object
- #>>(promise) ⇒ Object
- #^(promise) ⇒ Object
- #act? ⇒ Boolean
- #always(&block) ⇒ Object (also: #finally, #ensure)
- #exception!(error) ⇒ Object
- #exception? ⇒ Boolean
- #fail(&block) ⇒ Object (also: #rescue, #catch)
-
#initialize(success = nil, failure = nil) ⇒ Promise
constructor
A new instance of Promise.
- #inspect ⇒ Object
- #realized? ⇒ Boolean
- #reject(value = nil) ⇒ Object
- #reject!(value) ⇒ Object
- #rejected? ⇒ Boolean
- #resolve(value = nil) ⇒ Object
- #resolve!(value) ⇒ Object
- #resolved? ⇒ Boolean
- #then(&block) ⇒ Object (also: #do)
- #trace(depth = nil, &block) ⇒ Object
Constructor Details
#initialize(success = nil, failure = nil) ⇒ Promise
Returns a new instance of Promise
115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'opal/stdlib/promise.rb', line 115 def initialize(success = nil, failure = nil) @success = success @failure = failure @realized = nil @exception = false @value = nil @error = nil @delayed = nil @prev = nil @next = nil end |
Instance Attribute Details
#error ⇒ Object (readonly)
Returns the value of attribute error
113 114 115 |
# File 'opal/stdlib/promise.rb', line 113 def error @error end |
#next ⇒ Object (readonly)
Returns the value of attribute next
113 114 115 |
# File 'opal/stdlib/promise.rb', line 113 def next @next end |
#prev ⇒ Object (readonly)
Returns the value of attribute prev
113 114 115 |
# File 'opal/stdlib/promise.rb', line 113 def prev @prev end |
#value ⇒ Object (readonly)
Returns the value of attribute value
113 114 115 |
# File 'opal/stdlib/promise.rb', line 113 def value @value end |
Class Method Details
.error(value) ⇒ Object
105 106 107 |
# File 'opal/stdlib/promise.rb', line 105 def self.error(value) new.reject(value) end |
.value(value) ⇒ Object
101 102 103 |
# File 'opal/stdlib/promise.rb', line 101 def self.value(value) new.resolve(value) end |
.when(*promises) ⇒ Object
109 110 111 |
# File 'opal/stdlib/promise.rb', line 109 def self.when(*promises) When.new(promises) end |
Instance Method Details
#<<(promise) ⇒ Object
156 157 158 159 160 |
# File 'opal/stdlib/promise.rb', line 156 def <<(promise) @prev = promise self end |
#>>(promise) ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'opal/stdlib/promise.rb', line 162 def >>(promise) @next = promise if exception? promise.reject(@delayed) elsif resolved? promise.resolve(@delayed || value) elsif rejected? && (!@failure || Promise === (@delayed || @error)) promise.reject(@delayed || error) end self end |
#^(promise) ⇒ Object
149 150 151 152 153 154 |
# File 'opal/stdlib/promise.rb', line 149 def ^(promise) promise << self self >> promise promise end |
#always(&block) ⇒ Object Also known as: finally, ensure
277 278 279 280 281 282 283 |
# File 'opal/stdlib/promise.rb', line 277 def always(&block) if @next raise ArgumentError, 'a promise has already been chained' end self ^ Promise.new(block, block) end |
#exception!(error) ⇒ Object
250 251 252 253 254 |
# File 'opal/stdlib/promise.rb', line 250 def exception!(error) @exception = true reject!(error) end |
#exception? ⇒ Boolean
133 134 135 |
# File 'opal/stdlib/promise.rb', line 133 def exception? @exception end |
#fail(&block) ⇒ Object Also known as: rescue, catch
266 267 268 269 270 271 272 |
# File 'opal/stdlib/promise.rb', line 266 def fail(&block) if @next raise ArgumentError, 'a promise has already been chained' end self ^ Promise.new(nil, block) end |
#inspect ⇒ Object
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'opal/stdlib/promise.rb', line 296 def inspect result = "#<#{self.class}(#{object_id})" if @next result += " >> #{@next.inspect}" end if realized? result += ": #{(@value || @error).inspect}>" else result += ">" end result end |
#realized? ⇒ Boolean
137 138 139 |
# File 'opal/stdlib/promise.rb', line 137 def realized? @realized != nil end |
#reject(value = nil) ⇒ Object
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'opal/stdlib/promise.rb', line 211 def reject(value = nil) if realized? raise ArgumentError, 'the promise has already been realized' end if Promise === value value << @prev return value ^ self end @realized = :reject @error = value begin if @failure value = @failure.call(value) if Promise === value reject!(value) end else reject!(value) end rescue Exception => e exception!(e) end self end |
#reject!(value) ⇒ Object
242 243 244 245 246 247 248 |
# File 'opal/stdlib/promise.rb', line 242 def reject!(value) if @next @next.reject(value) else @delayed = value end end |
#rejected? ⇒ Boolean
145 146 147 |
# File 'opal/stdlib/promise.rb', line 145 def rejected? @realized == :reject end |
#resolve(value = nil) ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'opal/stdlib/promise.rb', line 176 def resolve(value = nil) if realized? raise ArgumentError, 'the promise has already been realized' end if Promise === value value << @prev return value ^ self end @realized = :resolve @value = value begin if @success value = @success.call(value) end resolve!(value) rescue Exception => e exception!(e) end self end |
#resolve!(value) ⇒ Object
203 204 205 206 207 208 209 |
# File 'opal/stdlib/promise.rb', line 203 def resolve!(value) if @next @next.resolve(value) else @delayed = value end end |
#resolved? ⇒ Boolean
141 142 143 |
# File 'opal/stdlib/promise.rb', line 141 def resolved? @realized == :resolve end |