class Rosarium::Promise
Public Class Methods
all(promises)
click to toggle source
# File lib/rosarium/promise.rb, line 66 def self.all(promises) return resolve([]) if promises.empty? deferred = defer promises = promises.dup waiting_for = promises.count mutex = Mutex.new check = lambda do |promise| if promise.fulfilled? # Hits zero iff all promises were fulfilled if mutex.synchronize { (waiting_for -= 1) == 0 } deferred.resolve(promises.map(&:value)) end else deferred.reject(promise.reason) end end promises.each do |promise| promise.send(:when_settled) { check.call(promise) } end deferred.promise end
all_settled(promises)
click to toggle source
# File lib/rosarium/promise.rb, line 43 def self.all_settled(promises) return resolve([]) if promises.empty? deferred = defer promises = promises.dup waiting_for = promises.count mutex = Mutex.new check = proc do # Includes both fulfilled and rejected, so always hits zero eventually if mutex.synchronize { (waiting_for -= 1) == 0 } deferred.resolve promises end end promises.each do |promise| promise.send(:when_settled, &check) end deferred.promise end
defer()
click to toggle source
# File lib/rosarium/promise.rb, line 8 def self.defer promise = new resolver = lambda do |value| promise.send(:try_settle, value, nil) nil # do not leak end rejecter = lambda do |reason| raise "reason must be an Exception" unless reason.is_a?(Exception) promise.send(:try_settle, nil, reason) nil # do not leak end Deferred.new(promise, resolver, rejecter) end
execute(&block)
click to toggle source
# File lib/rosarium/promise.rb, line 39 def self.execute(&block) @resolved.then(&block) end
new()
click to toggle source
# File lib/rosarium/promise.rb, line 93 def initialize @state = :pending @settling = false @mutex = Mutex.new @condition = ConditionVariable.new @when_settled = [] end
reject(reason)
click to toggle source
# File lib/rosarium/promise.rb, line 33 def self.reject(reason) deferred = defer deferred.reject(reason) deferred.promise end
resolve(value)
click to toggle source
# File lib/rosarium/promise.rb, line 25 def self.resolve(value) return value if value.is_a? Promise deferred = defer deferred.resolve(value) deferred.promise end
Public Instance Methods
fulfilled?()
click to toggle source
# File lib/rosarium/promise.rb, line 132 def fulfilled? state == :fulfilled end
inspect()
click to toggle source
# File lib/rosarium/promise.rb, line 123 def inspect synchronize do r = { state: @state } r[:value] = @value if @state == :fulfilled r[:reason] = @reason if @state == :rejected r end end
reason()
click to toggle source
# File lib/rosarium/promise.rb, line 110 def reason wait_until_settled synchronize { @reason } end
rejected?()
click to toggle source
# File lib/rosarium/promise.rb, line 136 def rejected? state == :rejected end
rescue(&block)
click to toggle source
# File lib/rosarium/promise.rb, line 164 def rescue(&block) self.then(block) end
state()
click to toggle source
# File lib/rosarium/promise.rb, line 101 def state synchronize { @state } end
then(on_rejected = nil, &on_fulfilled)
click to toggle source
# File lib/rosarium/promise.rb, line 140 def then(on_rejected = nil, &on_fulfilled) deferred = self.class.defer when_settled do EXECUTOR.submit do begin deferred.resolve( if fulfilled? # User-supplied code on_fulfilled ? on_fulfilled.call(value) : value else # User-supplied code on_rejected ? on_rejected.call(reason) : raise(reason) end ) rescue Exception => e deferred.reject e end end end deferred.promise end
value()
click to toggle source
# File lib/rosarium/promise.rb, line 105 def value wait_until_settled synchronize { @value } end
value!()
click to toggle source
# File lib/rosarium/promise.rb, line 115 def value! wait_until_settled synchronize do raise @reason if @state == :rejected @value end end
Protected Instance Methods
when_settled(&block)
click to toggle source
# File lib/rosarium/promise.rb, line 228 def when_settled(&block) immediate = synchronize do @when_settled << block if @state == :pending @state != :pending end block.call if immediate end
Private Instance Methods
settle(value, reason)
click to toggle source
Only called once
# File lib/rosarium/promise.rb, line 216 def settle(value, reason) synchronize do @state = (reason ? :rejected : :fulfilled) @value = value @reason = reason @condition.broadcast @when_settled.slice!(0, @when_settled.length) end.each(&:call) end
synchronize(&block)
click to toggle source
# File lib/rosarium/promise.rb, line 182 def synchronize(&block) @mutex.synchronize(&block) end
try_settle(value, reason)
click to toggle source
Can be called more than once
# File lib/rosarium/promise.rb, line 187 def try_settle(value, reason) settle_with = nil synchronize do return if @state != :pending || @settling if value.is_a? Promise @settling = true elsif reason.nil? settle_with = [value, nil] else settle_with = [nil, reason] end end if settle_with settle(*settle_with) else value.when_settled do if value.fulfilled? settle(value.value, nil) else settle(nil, value.reason) end end end end
wait_until_settled()
click to toggle source
# File lib/rosarium/promise.rb, line 173 def wait_until_settled synchronize do loop do return if @state != :pending @condition.wait @mutex end end end