Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#sync breaks when mixing Promise classes #18

Closed
haileys opened this issue Aug 8, 2016 · 1 comment
Closed

#sync breaks when mixing Promise classes #18

haileys opened this issue Aug 8, 2016 · 1 comment

Comments

@haileys
Copy link

haileys commented Aug 8, 2016

I'm running into a bug in promise.rb where the sync method breaks when mixing various Promise classes together.

Here's some sample code demonstrating the problem:

require "promise"

class MyPromise < ::Promise
  def wait
    fulfill(456)
  end
end

p1 = Promise.resolve(123)

p2a = MyPromise.new
p2a.sync

p2b = MyPromise.new

p3a = p1.then { |a| p2a.then { |b| a + b } }
p3b = p1.then { |a| p2b.then { |b| a + b } }

puts p3a.sync
puts p3b.sync

What's happening is that when then is called on p1 (which is already resolved), promise.rb internally creates a new promise of the same type as p1 and then immediately executes the then block. Even though the then block is called immediately and returns a new MyPromise instance, the internal state of the promise returned by the then block is copied into the newly created Promise instance rather than the MyPromise instance being returned directly.

If p2a is also already resolved, this is not a problem and calling p3a.sync returns the right thing.

If p2b is not already resolved when p3b.sync is called, the sync call tries to call wait on an instance of Promise, rather than an instance of MyPromise. This raises a NoMethodError.

The output I'd expect the code sample above to produce is:

579
579

The actual output is:

579
lib/promise.rb:64:in `sync': undefined local variable or method `wait' for #<Promise:0x007fb8db51e028 @state=:pending, @callbacks=[]> (NameError)
    from x.rb:20:in `<main>'
@dylanahsmith
Copy link
Collaborator

Yes, that is the problem with mixing promise classes. This can be avoided by using p1 = MyPromise.resolve(123) so that you don't mix promise classes.

However, I don't think this is a bug.

To start with, you have an incorrect assumption that the block passed to then is executed immediately when the receiver Promise instance has already been resolved. The promise spec expects the onFulfilled or onRejected to be deferred. Promise#defer can be overridden to implement this behaviour and conform to the spec as documented in the README's usage section. This prevents the Promise#then method from being able to determine the class of a promise that may be returned from the block.

The other problem with what you are suggesting is that it would introduce racey behaviour in the type of value that is returned from Promise#then in code where the Promise instance could be resolved by another fiber or thread that is running in parallel. I would much prefer to have Promise#then deterministically return a result based on the type of the receiver so that it is easier to catch errors from misuse during testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants