[フレーム]

Class: PromiseV2

Inherits:
Object show all
Defined in:
opal/stdlib/promise/v2.rb

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/v2'

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 = PromiseV2.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 = PromiseV2.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 = PromiseV2.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 PromiseV2 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

PromiseV2.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:

PromiseV2.when(get_json, get_json2).then |first, second|
 puts "got two json payloads: #{first}, #{second}"
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize {|_self| ... } ⇒ PromiseV2

Returns a new instance of PromiseV2.

Yields:

  • (_self)

Yield Parameters:

  • _self (PromiseV2 )

    the object that the method was called on

344
345
346
# File 'opal/stdlib/promise/v2.rb', line 344
def initialize(&block)
 yield self if block_given?
end

Instance Attribute Details

#nextObject (readonly)

Returns the value of attribute next.

166
167
168
# File 'opal/stdlib/promise/v2.rb', line 166
def next
 @next
end

#prevObject (readonly)

Returns the value of attribute prev.

166
167
168
# File 'opal/stdlib/promise/v2.rb', line 166
def prev
 @prev
end

Class Method Details

.all_resolved(*promises) ⇒ Object

122
123
124
125
126
127
# File 'opal/stdlib/promise/v2.rb', line 122
def all_resolved(*promises)
 promises = Array (promises.length == 1 ? promises.first : promises)
 `Promise.allResolved(#{promises})`.tap do |prom|
 prom.instance_variable_set(:@type, :all_resolved)
 end
end

.allocateObject

105
106
107
108
109
110
111
112
113
# File 'opal/stdlib/promise/v2.rb', line 105
def allocate
 ok, fail = nil, nil
 prom = `new self.$$constructor(function(_ok, _fail) { #{ok} = _ok; #{fail} = _fail; })`
 prom.instance_variable_set(:@type, :opal)
 prom.instance_variable_set(:@resolve_proc, ok)
 prom.instance_variable_set(:@reject_proc, fail)
 prom
end

.any(*promises) ⇒ Object

129
130
131
132
133
134
# File 'opal/stdlib/promise/v2.rb', line 129
def any(*promises)
 promises = Array (promises.length == 1 ? promises.first : promises)
 `Promise.any(#{promises})`.tap do |prom|
 prom.instance_variable_set(:@type, :any)
 end
end

.errorObject

162
163
164
165
166
167
168
169
# File 'opal/stdlib/promise/v2.rb', line 162
def reject(value = nil)
 `Promise.reject(#{value})`.tap do |prom|
 prom.instance_variable_set(:@type, :reject)
 prom.instance_variable_set(:@realized, :reject)
 prom.instance_variable_set(:@value_set, true)
 prom.instance_variable_set(:@value, value)
 end
end

.race(*promises) ⇒ Object

136
137
138
139
140
141
# File 'opal/stdlib/promise/v2.rb', line 136
def race(*promises)
 promises = Array (promises.length == 1 ? promises.first : promises)
 `Promise.race(#{promises})`.tap do |prom|
 prom.instance_variable_set(:@type, :race)
 end
end

.reject(value = nil) ⇒ Object

152
153
154
155
156
157
158
159
# File 'opal/stdlib/promise/v2.rb', line 152
def reject(value = nil)
 `Promise.reject(#{value})`.tap do |prom|
 prom.instance_variable_set(:@type, :reject)
 prom.instance_variable_set(:@realized, :reject)
 prom.instance_variable_set(:@value_set, true)
 prom.instance_variable_set(:@value, value)
 end
end

.resolve(value = nil) ⇒ Object

143
144
145
146
147
148
149
150
# File 'opal/stdlib/promise/v2.rb', line 143
def resolve(value = nil)
 `Promise.resolve(#{value})`.tap do |prom|
 prom.instance_variable_set(:@type, :resolve)
 prom.instance_variable_set(:@realized, :resolve)
 prom.instance_variable_set(:@value_set, true)
 prom.instance_variable_set(:@value, value)
 end
end

.valueObject

163
164
165
166
167
168
169
170
# File 'opal/stdlib/promise/v2.rb', line 163
def resolve(value = nil)
 `Promise.resolve(#{value})`.tap do |prom|
 prom.instance_variable_set(:@type, :resolve)
 prom.instance_variable_set(:@realized, :resolve)
 prom.instance_variable_set(:@value_set, true)
 prom.instance_variable_set(:@value, value)
 end
end

.when(*promises) ⇒ Object Also known as: all

115
116
117
118
119
120
# File 'opal/stdlib/promise/v2.rb', line 115
def when(*promises)
 promises = Array (promises.length == 1 ? promises.first : promises)
 `Promise.all(#{promises})`.tap do |prom|
 prom.instance_variable_set(:@type, :when)
 end
end

Instance Method Details

#always(&block) ⇒ Object Also known as: ensure, finally

259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'opal/stdlib/promise/v2.rb', line 259
def always(&block)
 prom = nil
 blk = gen_tracing_proc(block) do |val|
 prom.instance_variable_set(:@realized, :resolve)
 prom.instance_variable_set(:@value_set, true)
 prom.instance_variable_set(:@value, val)
 end
 prom = `self.finally(function() { return blk(self.$value_internal()); })`
 prom.instance_variable_set(:@prev, self)
 prom.instance_variable_set(:@type, :always)
 (@next ||= []) << prom
 prom
end

#always!(&block) ⇒ Object Also known as: ensure!, finally!

273
274
275
276
# File 'opal/stdlib/promise/v2.rb', line 273
def always!(&block)
 there_can_be_only_one!
 always(&block)
end

#and(*promises) ⇒ Object

331
332
333
334
335
336
337
338
339
340
341
342
# File 'opal/stdlib/promise/v2.rb', line 331
def and(*promises)
 promises = promises.map do |i|
 if PromiseV2  === i
 i
 else
 PromiseV2 .value (i)
 end
 end
 PromiseV2 .when (self, *promises).then do |a, *b|
 [*a, *b]
 end
end

#errorObject

326
327
328
329
# File 'opal/stdlib/promise/v2.rb', line 326
def error
 light_nativity_check!
 @value if rejected?
end

#fail(&block) ⇒ Object Also known as: catch, rescue

240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'opal/stdlib/promise/v2.rb', line 240
def fail(&block)
 prom = nil
 blk = gen_tracing_proc(block) do |val|
 prom.instance_variable_set(:@realized, :resolve)
 prom.instance_variable_set(:@value_set, true)
 prom.instance_variable_set(:@value, val)
 end
 prom = `self.catch(#{blk})`
 prom.instance_variable_set(:@prev, self)
 prom.instance_variable_set(:@type, :fail)
 (@next ||= []) << prom
 prom
end

#fail!(&block) ⇒ Object Also known as: catch!, rescue!

254
255
256
257
# File 'opal/stdlib/promise/v2.rb', line 254
def fail!(&block)
 there_can_be_only_one!
 fail(&block)
end

#gen_tracing_proc(passing, &block) ⇒ Object

193
194
195
196
197
198
199
# File 'opal/stdlib/promise/v2.rb', line 193
def gen_tracing_proc(passing, &block)
 proc do |i|
 res = passing.call(i)
 yield(res)
 res
 end
end

#inspectObject

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'opal/stdlib/promise/v2.rb', line 356
def inspect
 result = "#<#{self.class}"
 if @type
 result += ":#{@type}" unless %i[opalresolvereject].include? @type
 else
 result += ':native'
 end
 result += ":#{@realized}" if @realized
 result += "(#{object_id})"
 if @next && @next.any?
 result += " >> #{@next.inspect}"
 end
 result += ": #{value.inspect}"
 result += '>'
 result
end

#light_nativity_check!Object

Raise an exception when a non-JS-native method is called on a JS-native promise but permits some typed promises

Raises:

  • (ArgumentError)
181
182
183
184
# File 'opal/stdlib/promise/v2.rb', line 181
def light_nativity_check!
 return if %i[rejectresolvetracealwaysfailthen].include? @type
 raise ArgumentError, 'this promise is native to JavaScript' if native?
end

#native?Boolean

Is this promise native to JavaScript? This means, that methods like resolve or reject won't be available.

Returns:

170
171
172
# File 'opal/stdlib/promise/v2.rb', line 170
def native?
 @type != :opal
end

#nativity_check!Object

Raise an exception when a non-JS-native method is called on a JS-native promise

Raises:

  • (ArgumentError)
175
176
177
# File 'opal/stdlib/promise/v2.rb', line 175
def nativity_check!
 raise ArgumentError, 'this promise is native to JavaScript' if native?
end

#realized?Boolean

Returns:

315
316
317
318
# File 'opal/stdlib/promise/v2.rb', line 315
def realized?
 light_nativity_check!
 !@realized.nil?
end

#reject(value = nil) ⇒ Object Also known as: reject!

Raises:

  • (ArgumentError)
211
212
213
214
215
216
217
218
219
# File 'opal/stdlib/promise/v2.rb', line 211
def reject(value = nil)
 nativity_check!
 raise ArgumentError, 'this promise was already resolved' if @realized
 @value_set = true
 @value = value
 @realized = :reject
 @reject_proc.call(value)
 self
end

#rejected?Boolean

Returns:

310
311
312
313
# File 'opal/stdlib/promise/v2.rb', line 310
def rejected?
 light_nativity_check!
 @realized == :reject
end

#resolve(value = nil) ⇒ Object Also known as: resolve!

Raises:

  • (ArgumentError)
201
202
203
204
205
206
207
208
209
# File 'opal/stdlib/promise/v2.rb', line 201
def resolve(value = nil)
 nativity_check!
 raise ArgumentError, 'this promise was already resolved' if @realized
 @value_set = true
 @value = value
 @realized = :resolve
 @resolve_proc.call(value)
 self
end

#resolved?Boolean

Returns:

305
306
307
308
# File 'opal/stdlib/promise/v2.rb', line 305
def resolved?
 light_nativity_check!
 @realized == :resolve
end

#then(&block) ⇒ Object Also known as: do

221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'opal/stdlib/promise/v2.rb', line 221
def then(&block)
 prom = nil
 blk = gen_tracing_proc(block) do |val|
 prom.instance_variable_set(:@realized, :resolve)
 prom.instance_variable_set(:@value_set, true)
 prom.instance_variable_set(:@value, val)
 end
 prom = `self.then(#{blk})`
 prom.instance_variable_set(:@prev, self)
 prom.instance_variable_set(:@type, :then)
 (@next ||= []) << prom
 prom
end

#then!(&block) ⇒ Object Also known as: do!

235
236
237
238
# File 'opal/stdlib/promise/v2.rb', line 235
def then!(&block)
 there_can_be_only_one!
 self.then(&block)
end

#there_can_be_only_one!Object

Allow only one chain to be present, as needed by the previous implementation. This isn't a strict check - it's always possible on the JS side to chain a given block.

Raises:

  • (ArgumentError)
189
190
191
# File 'opal/stdlib/promise/v2.rb', line 189
def there_can_be_only_one!
 raise ArgumentError, 'a promise has already been chained' if @next && @next.any?
end

#to_v1Object

348
349
350
351
352
353
354
# File 'opal/stdlib/promise/v2.rb', line 348
def to_v1
 v1 = PromiseV1 .new
 self.then { |i| v1.resolve(i) }.rescue { |i| v1.reject(i) }
 v1
end

#trace(depth = nil, &block) ⇒ Object

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'opal/stdlib/promise/v2.rb', line 278
def trace(depth = nil, &block)
 prom = self.then do
 values = []
 prom = self
 while prom && (!depth || depth > 0)
 val = nil
 begin
 val = prom.value
 rescue ArgumentError
 val = :native
 end
 values.unshift(val)
 depth -= 1 if depth
 prom = prom.prev
 end
 yield(*values)
 end
 prom.instance_variable_set(:@type, :trace)
 prom
end

#trace!(*args, &block) ⇒ Object

300
301
302
303
# File 'opal/stdlib/promise/v2.rb', line 300
def trace!(*args, &block)
 there_can_be_only_one!
 trace(*args, &block)
end

#valueObject

320
321
322
323
324
# File 'opal/stdlib/promise/v2.rb', line 320
def value
 if resolved?
 value_internal
 end
end

AltStyle によって変換されたページ (->オリジナル) /