module DeferrableGratification::Combinators::ClassMethods

Combinators which don’t make sense as methods on Deferrable.

{DeferrableGratification} extends this module, and thus the methods here are accessible via the {DG} alias.

Public Instance Methods

chain(*actions) click to toggle source

Execute a sequence of asynchronous operations that may each depend on the result of the previous operation.

@see bind! more detail on the semantics.

@param [*Proc] *actions procs that will perform an operation and

return a Deferrable.

@return [Deferrable] Deferrable that will succeed if all of the

chained operations succeeded, and callback with the result of the
last operation.
# File lib/deferrable_gratification/combinators.rb, line 241
def chain(*actions)
  actions.inject(DG.const(nil), &:>>)
end
join_first_success(*operations) click to toggle source

Combinator that waits for any of the supplied asynchronous operations to succeed, and succeeds with the result of the first (chronologically) to do so.

This Deferrable will fail if all the operations fail. It may never succeed or fail, if one of the operations also does not.

@param (see join_successes)

@return [Deferrable] a deferred status that will succeed as soon as any

of the +operations+ succeeds; its callbacks will be passed the result
of that operation.
# File lib/deferrable_gratification/combinators.rb, line 279
def join_first_success(*operations)
  Join::FirstSuccess.setup!(*operations)
end
join_successes(*operations) click to toggle source

Combinator that waits for all of the supplied asynchronous operations to succeed or fail, then succeeds with the results of all those operations that were successful.

This Deferrable will never fail. It may also never succeed, if any of the supplied operations does not either succeed or fail.

The successful results are guaranteed to be in the same order as the operations were passed in (which may not be the same as the chronological order in which they succeeded).

@param [*Deferrable] *operations deferred statuses of asynchronous

operations to wait for.

@return [Deferrable] a deferred status that will succeed after all the

+operations+ have either succeeded or failed; its callbacks will be
passed an +Enumerable+ containing the results of those operations
that succeeded.
# File lib/deferrable_gratification/combinators.rb, line 263
def join_successes(*operations)
  Join::Successes.setup!(*operations)
end
loop_until_success(loop_deferrable = DefaultDeferrable.new) { || ... } click to toggle source

Combinator that repeatedly executes the supplied block until it succeeds, then succeeds itself with the eventual result.

This Deferrable may never succeed, if the operation never succeeds. It will fail if an iteration raises an exception.

@note this combinator is intended for use inside EventMachine. It will

still work outside of EventMachine, _provided_ that the operation is
synchronous (although a simple +while+ loop might be preferable in
this case!).

@param loop_deferrable for internal use only, always omit this. @param &block operation to execute until it succeeds.

@yieldreturn [Deferrable] deferred status of the operation. If it

fails, the operation will be retried.  If it succeeds, the combinator
will succeed with the result.

@return [Deferrable] a deferred status that will succeed once the

supplied operation eventually succeeds.
# File lib/deferrable_gratification/combinators.rb, line 303
def loop_until_success(loop_deferrable = DefaultDeferrable.new, &block)
  if EM.reactor_running?
    EM.next_tick do
      begin
        attempt = yield
      rescue => e
        loop_deferrable.fail(e)
      else
        attempt.callback(&loop_deferrable.method(:succeed))
        attempt.errback { loop_until_success(loop_deferrable, &block) }
      end
    end
  else
    # In the synchronous case, we could simply use the same
    # implementation as in EM, but without the next_tick; unfortunately
    # that means direct recursion, so risks stack overflow.  Instead we
    # just reimplement as a loop.
    results = []
    begin
      yield.callback {|*values| results << values } while results.empty?
    rescue => e
      loop_deferrable.fail(e)
    else
      loop_deferrable.succeed(*results[0])
    end
  end
  loop_deferrable
end