
PurePromise
My promises library. It tries to be as close to the Promises/A+ spec as possible, with one exception:
A promise callback must return a promise
This makes it slightly more verbose, but it has some nice properties. I'll explain them here later.
Influenced by promise.rb, the Promises/A+ spec, and browsers' implementations of promises (for the stuff that's not #then
).
Installation
Add this line to your application's Gemfile:
gem 'pure_promise'
And then execute:
$ bundle
Or install it yourself as:
$ gem install pure_promise
Usage
Making them asynchronous
Note: The defer method does not have to yield in the order in which defer was called,
it just has to yield some time in the future.
class EMPromise < PurePromise
def defer
EM.next_tick { yield }
end
end
Creating promises
PurePromise.fulfill(:value)
PurePromise.fulfill
PurePromise.reject(:value)
PurePromise.reject
PurePromise.new
PurePromise.new do |fulfill, reject|
if something?
fulfill.call(:value)
else
reject.call(:error)
end
end
PurePromise.resolve(thenable)
PurePromise.error
PurePromise.error('message')
PurePromise.error(TypeError, 'message')
PurePromise.error(TypeError.new('message'))
Mutating promises
A promise can only be mutated once. Once it has transitioned from pending, the value cannot be changed.
promise = Promise.new
promise.fulfill(:value)
promise.reject(:value)
promise.resolve(thenable)
Accessing promises
The only way to access a promise's value is through the then/catch methods.
Each callback must evaluate to a promise. If the action in the callback succeeds, return PurePromise.fulfill
,
otherwise return PurePromise.reject
.
then
and catch
always return a promise, which fulfills or rejects to the value of the promise returned from the callback when it is executed.
If a callback raises an error, the promise returned by then
or catch
will be rejected with the error as the value.
PurePromise.fulfill(:some_value).then do |value|
puts value.inspect
PurePromise.fulfill
end
PurePromise.error.catch do |error|
puts error.inspect
PurePromise.fulfill
end
PurePromise.fulfill(:some_value).then(proc { |value|
puts value.inspect
PurePromise.fulfill
}, proc { |error|
puts error.inspect
PurePromise.fulfill
})
Shortcomings addressed
This isn't having a dig at anyone else's work, these are just the reasons why I wanted to create my own promises library.
I could have got a lot of things wrong too, and I'd love to hear about them in the issues section.
In Promises/A+ Spec
- You cannot wrap anything that implements a
then
method in a promise.
This bit me when wanting to pass around a faye client in a promise system - and it took me forever to debug.
PurePromise addresses this by forcing you to return a promise from your callbacks.
In Promise.rb
- IMO, being able to retrieve the value of the promise through an accessor is wrong.
What do you return when the promise is pending and has no value? Nil? But nil is a valid value for a promise,
creating ambiguity.
Design goals
- Address the above shortcomings.
- Limit the public api to as small as possible (then, fulfill, reject, resolve).
Everything else should just be convenience methods on top of these.
TODO
- DRY up specs; they are pretty verbose atm.
- Get 100% mutation coverage
- Release gem
Contributing
- Fork it ( https://github.com/cameron-martin/pure_promise/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request