class Tilia::Event::Promise
An implementation of the Promise
pattern.
Promises basically allow you to avoid what is commonly called 'callback hell'. It allows for easily chaining of asynchronous operations.
Constants
- FULFILLED
The promise has been fulfilled. It was successful.
- PENDING
Pending promise. No result yet.
- REJECTED
The promise was rejected. The operation failed.
Public Class Methods
It's possible to send an array of promises to the all method. This method returns a promise that will be fulfilled, only if all the passed promises are fulfilled.
@param [Array<Promise>] promises @return [Promise]
# File lib/tilia/event/promise.rb, line 116 def self.all(promises) new( lambda do |success, failing| success_count = 0 complete_result = [] promises.each_with_index do |sub_promise, promise_index| sub_promise.then( lambda do |result| complete_result[promise_index] = result success_count += 1 success.call(complete_result) if success_count == promises.size return result end ).error( lambda do |reason| failing.call(reason) end ) end end ) end
Creates the promise.
The passed argument is the executor. The executor is automatically called with two arguments.
Each are callbacks that map to self.fulfill and self.reject. Using the executor is optional.
@param [#call, nil] executor @return [void]
# File lib/tilia/event/promise.rb, line 27 def initialize(executor = nil) @state = PENDING @subscribers = [] @value = nil executor.call(method(:fulfill), method(:reject)) if executor end
Public Instance Methods
Add a callback for when this promise is rejected.
I would have used the word 'catch', but it's a reserved word in PHP, so we're not allowed to call our function that.
@param [#call] on_rejected @return [Promise]
# File lib/tilia/event/promise.rb, line 76 def error(on_rejected) self.then(nil, on_rejected) end
Marks this promise as fulfilled and sets its return value.
@param value @return [void]
# File lib/tilia/event/promise.rb, line 84 def fulfill(value = nil) unless @state == PENDING fail PromiseAlreadyResolvedException, 'This promise is already resolved, and you\'re not allowed to resolve a promise more than once' end @state = FULFILLED @value = value @subscribers.each do |subscriber| invoke_callback(subscriber[0], subscriber[1]) end end
Marks this promise as rejected, and set it's rejection reason.
@param reason @return [void]
# File lib/tilia/event/promise.rb, line 99 def reject(reason = nil) unless @state == PENDING fail PromiseAlreadyResolvedException, 'This promise is already resolved, and you\'re not allowed to resolve a promise more than once' end @state = REJECTED @value = reason @subscribers.each do |subscriber| invoke_callback(subscriber[0], subscriber[2]) end end
This method allows you to specify the callback that will be called after the promise has been fulfilled or rejected.
Both arguments are optional.
This method returns a new promise, which can be used for chaining. If either the onFulfilled or onRejected callback is called, you may return a result from this callback.
If the result of this callback is yet another promise, the result of that promise will be used to set the result of the returned promise.
If either of the callbacks return any other value, the returned promise is automatically fulfilled with that value.
If either of the callbacks throw an exception, the returned promise will be rejected and the exception will be passed back.
@param [#call, nil] on_fulfilled @param [#call, nil] on_rejected @return [Promise]
# File lib/tilia/event/promise.rb, line 56 def then(on_fulfilled = nil, on_rejected = nil) sub_promise = Promise.new case @state when PENDING @subscribers << [sub_promise, on_fulfilled, on_rejected] when FULFILLED invoke_callback(sub_promise, on_fulfilled) when REJECTED invoke_callback(sub_promise, on_rejected) end sub_promise end
Protected Instance Methods
This method is used to call either an onFulfilled or onRejected callback.
This method makes sure that the result of these callbacks are handled correctly, and any chained promises are also correctly fulfilled or rejected.
@param [Promise] sub_promise @param [#call, nil] call_back @return [void]
# File lib/tilia/event/promise.rb, line 153 def invoke_callback(sub_promise, call_back = nil) if call_back.is_a?(Proc) || call_back.is_a?(Method) begin result = call_back.call(@value) if result.is_a?(Promise) result.then(sub_promise.method(:fulfill), sub_promise.method(:reject)) else sub_promise.fulfill(result) end rescue => e sub_promise.reject(e.to_s) end elsif @state == FULFILLED sub_promise.fulfill(@value) else sub_promise.reject(@value) end end