Module: EventMachine::Deferrable
- Included in:
- DefaultDeferrable, DeferrableChildProcess, FileStreamer, Protocols::HttpClient, Protocols::HttpClient2::Request, Protocols::Memcache, Protocols::SmtpClient, Protocols::TcpConnectTester
- Defined in:
- lib/em/future.rb,
lib/em/deferrable.rb
Class Method Summary collapse
-
.future(arg, cb = nil, eb = nil, &blk) ⇒ Object
A future is a sugaring of a typical deferrable usage.
Instance Method Summary collapse
-
#callback(&block) ⇒ Object
Specify a block to be executed if and when the Deferrable object receives a status of :succeeded.
-
#cancel_timeout ⇒ Object
Cancels an outstanding timeout if any.
-
#errback(&block) ⇒ Object
Specify a block to be executed if and when the Deferrable object receives a status of :failed.
-
#fail(*args) ⇒ Object
(also: #set_deferred_failure)
Sugar for set_deferred_status(:failed, …).
-
#set_deferred_status(status, *args) ⇒ Object
Sets the “disposition” (status) of the Deferrable object.
-
#succeed(*args) ⇒ Object
(also: #set_deferred_success)
Sugar for set_deferred_status(:succeeded, …).
-
#timeout(seconds) ⇒ Object
Setting a timeout on a Deferrable causes it to go into the failed state after the Timeout expires (passing no arguments to the object’s errbacks).
Class Method Details
.future(arg, cb = nil, eb = nil, &blk) ⇒ Object
A future is a sugaring of a typical deferrable usage. – Evaluate arg (which may be an expression or a block). What’s the class of arg? If arg is an ordinary expression, then return it. If arg is deferrable (responds to :set_deferred_status), then look at the arguments. If either callback or errback are defined, then use them. If neither are defined, then use the supplied block (if any) as the callback. Then return arg.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/em/future.rb', line 44 def self.future arg, cb=nil, eb=nil, &blk arg = arg.call if arg.respond_to?(:call) if arg.respond_to?(:set_deferred_status) if cb || eb arg.callback(&cb) if cb arg.errback(&eb) if eb else arg.callback(&blk) if blk end end arg end |
Instance Method Details
#callback(&block) ⇒ Object
Specify a block to be executed if and when the Deferrable object receives a status of :succeeded. See #set_deferred_status for more information.
Calling this method on a Deferrable object whose status is not yet known will cause the callback block to be stored on an internal list. If you call this method on a Deferrable whose status is :succeeded, the block will be executed immediately, receiving the parameters given to the prior #set_deferred_status call.
– If there is no status, add a callback to an internal list. If status is succeeded, execute the callback immediately. If status is failed, do nothing.
43 44 45 46 47 48 49 50 51 52 |
# File 'lib/em/deferrable.rb', line 43 def callback &block return unless block @deferred_status ||= :unknown if @deferred_status == :succeeded block.call(*@deferred_args) elsif @deferred_status != :failed @callbacks ||= [] @callbacks.unshift block # << block end end |
#cancel_timeout ⇒ Object
Cancels an outstanding timeout if any. Undoes the action of #timeout.
161 162 163 164 165 166 167 |
# File 'lib/em/deferrable.rb', line 161 def cancel_timeout @deferred_timeout ||= nil if @deferred_timeout @deferred_timeout.cancel @deferred_timeout = nil end end |
#errback(&block) ⇒ Object
Specify a block to be executed if and when the Deferrable object receives a status of :failed. See #set_deferred_status for more information. – If there is no status, add an errback to an internal list. If status is failed, execute the errback immediately. If status is succeeded, do nothing.
61 62 63 64 65 66 67 68 69 70 |
# File 'lib/em/deferrable.rb', line 61 def errback &block return unless block @deferred_status ||= :unknown if @deferred_status == :failed block.call(*@deferred_args) elsif @deferred_status != :succeeded @errbacks ||= [] @errbacks.unshift block # << block end end |
#fail(*args) ⇒ Object Also known as: set_deferred_failure
Sugar for set_deferred_status(:failed, …)
179 180 181 |
# File 'lib/em/deferrable.rb', line 179 def fail *args set_deferred_status :failed, *args end |
#set_deferred_status(status, *args) ⇒ Object
Sets the “disposition” (status) of the Deferrable object. See also the large set of sugarings for this method. Note that if you call this method without arguments, no arguments will be passed to the callback/errback. If the user has coded these with arguments, then the user code will throw an argument exception. Implementors of deferrable classes must document the arguments they will supply to user callbacks.
OBSERVE SOMETHING VERY SPECIAL here: you may call this method even on the INSIDE of a callback. This is very useful when a previously-registered callback wants to change the parameters that will be passed to subsequently-registered ones.
You may give either :succeeded or :failed as the status argument.
If you pass :succeeded, then all of the blocks passed to the object using the #callback method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks passed to the object using #errback will be discarded.
If you pass :failed, then all of the blocks passed to the object using the #errback method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks passed to the object using # callback will be discarded.
If you pass any arguments to #set_deferred_status in addition to the status argument, they will be passed as arguments to any callbacks or errbacks that are executed. It’s your responsibility to ensure that the argument lists specified in your callbacks and errbacks match the arguments given in calls to #set_deferred_status, otherwise Ruby will raise an ArgumentError.
– We’re shifting callbacks off and discarding them as we execute them. This is valid because by definition callbacks are executed no more than once. It also has the magic effect of permitting recursive calls, which means that a callback can call #set_deferred_status and change the parameters that will be sent to subsequent callbacks down the chain.
Changed @callbacks and @errbacks from push/shift to unshift/pop, per suggestion by Kirk Haines, to work around the memory leak bug that still exists in many Ruby versions.
Changed 15Sep07: after processing callbacks or errbacks, CLEAR the other set of handlers. This gets us a little closer to the behavior of Twisted’s “deferred,” which only allows status to be set once. Prior to making this change, it was possible to “succeed” a Deferrable (triggering its callbacks), and then immediately “fail” it, triggering its errbacks! That is clearly undesirable, but it’s just as undesirable to raise an exception is status is set more than once on a Deferrable. The latter behavior would invalidate the idiom of resetting arguments by setting status from within a callback or errback, but more seriously it would cause spurious errors if a Deferrable was timed out and then an attempt was made to succeed it. See the comments under the new method #timeout.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/em/deferrable.rb', line 124 def set_deferred_status status, *args cancel_timeout @errbacks ||= nil @callbacks ||= nil @deferred_status = status @deferred_args = args case @deferred_status when :succeeded if @callbacks while cb = @callbacks.pop cb.call(*@deferred_args) end end @errbacks.clear if @errbacks when :failed if @errbacks while eb = @errbacks.pop eb.call(*@deferred_args) end end @callbacks.clear if @callbacks end end |
#succeed(*args) ⇒ Object Also known as: set_deferred_success
Sugar for set_deferred_status(:succeeded, …)
172 173 174 |
# File 'lib/em/deferrable.rb', line 172 def succeed *args set_deferred_status :succeeded, *args end |
#timeout(seconds) ⇒ Object
Setting a timeout on a Deferrable causes it to go into the failed state after the Timeout expires (passing no arguments to the object’s errbacks). Setting the status at any time prior to a call to the expiration of the timeout will cause the timer to be cancelled.
153 154 155 156 157 |
# File 'lib/em/deferrable.rb', line 153 def timeout seconds cancel_timeout me = self @deferred_timeout = EventMachine::Timer.new(seconds) {me.fail} end |