Skip to content

Commit

Permalink
refactor: Allow a promise to be used as a callback
Browse files Browse the repository at this point in the history
Avoids unnecessary object allocation when fulfilling a promise using a
callback.
  • Loading branch information
dylanahsmith committed Nov 9, 2016
1 parent d66588e commit 3545029
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 31 deletions.
30 changes: 19 additions & 11 deletions lib/promise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def then(on_fulfill = nil, on_reject = nil, &block)
on_fulfill ||= block
next_promise = self.class.new

add_callback(Callback.new(self, on_fulfill, on_reject, next_promise))
add_callback(Callback.new(on_fulfill, on_reject, next_promise))
next_promise
end

Expand All @@ -68,7 +68,7 @@ def sync

def fulfill(value = nil)
if Promise === value
Callback.assume_state(value, self)
value.add_callback(self)
else
dispatch do
@state = :fulfilled
Expand All @@ -89,6 +89,16 @@ def defer
yield
end

protected

def add_callback(callback)
if pending?
@callbacks << callback
else
dispatch!(callback)
end
end

private

def reason_coercion(reason)
Expand All @@ -101,14 +111,6 @@ def reason_coercion(reason)
reason
end

def add_callback(callback)
if pending?
@callbacks << callback
else
dispatch!(callback)
end
end

def dispatch
if pending?
yield
Expand All @@ -118,6 +120,12 @@ def dispatch
end

def dispatch!(callback)
defer { callback.call }
defer do
if fulfilled?
callback.fulfill(value)
else
callback.reject(reason)
end
end
end
end
37 changes: 17 additions & 20 deletions lib/promise/callback.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,35 @@

class Promise
class Callback
def self.assume_state(source, target)
on_fulfill = target.public_method(:fulfill)
on_reject = target.public_method(:reject)
source.then(on_fulfill, on_reject)
end

def initialize(promise, on_fulfill, on_reject, next_promise)
@promise = promise
def initialize(on_fulfill, on_reject, next_promise)
@on_fulfill = on_fulfill
@on_reject = on_reject
@next_promise = next_promise
end

def call
if @promise.fulfilled?
call_block(@on_fulfill, @promise.value)
def fulfill(value)
if @on_fulfill
call_block(@on_fulfill, value)
else
call_block(@on_reject, @promise.reason)
@next_promise.fulfill(value)
end
end

def call_block(block, param)
if block
begin
@next_promise.fulfill(block.call(param))
rescue => ex
@next_promise.reject(ex)
end
def reject(reason)
if @on_reject
call_block(@on_reject, reason)
else
self.class.assume_state(@promise, @next_promise)
@next_promise.reject(reason)
end
end

private

def call_block(block, param)
@next_promise.fulfill(block.call(param))
rescue => ex
@next_promise.reject(ex)
end
end
private_constant :Callback
end

0 comments on commit 3545029

Please sign in to comment.