Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I needed several classes with methods performing network requests which should be executed asynchronously (with callbacks). To get rid of repetition, I added a mixin and a helper class:

module Asyncable
  def async(&callback)
    AsyncHelper.new(self, &callback)
  end
end

class AsyncHelper
  attr_reader :base, :callback, :errback

  def initialize(base, &callback)
    @base = base
    @callback = callback
    @errback = nil
  end

  def onerror!(&errback)
    @errback = errback
    self
  end

  def method_missing(sym, *args)
    method = @base.method(sym)
    Thread.new do
      begin
        res = method.call(*args)
      rescue => e
        if @errback.nil?
          raise
        else
          @errback.call(e)
        end
      else
        unless @callback.nil?
          @callback.call(res)
        end
        res
      end
    end
  end
end

Now all methods in classes can be implemented as synchronous, and then used in these ways:

# assuming base is an instance of a class including Asyncable
# sequential execution of a method:
value = base.method(params)
f(value) # process the value

# same method executed asynchronously, without callback:
thread = base.async.method(params)
# ... other operations while the method is executed in the background ...
value = thread.value # blocks until completed and raises errors, if any
f(value)

# same method executed asynchronously, with callback:
thread = base.async { |value| f(value) }.method(params)
# ... other operations while the method is executed in the background ...
thread.join # blocks until completed and raises errors, if any

# using errback to handle errors:
thread = base.async { |value| f(value) }.onerror! { |e| puts e }.method(params)
# ... other operations while the method is executed in the background ...
thread.join # only blocks, no errors raised here

This works perfectly for me, but I am a bit concerned by the fact that such an approach isn't widely used (I haven't seen it anywhere), while the use-case for it may be common enough. Is it a good way to approach the task, and how can be improved?

share|improve this question
    
Are you aware of EventMachine? github.com/igrigorik/em-http-request/wiki/Issuing-Requests –  tokland Mar 23 at 17:30
    
@tokland: I heard about it, but I had a requirement that calling with a callback specified should be supported. And my solution gives plain sequential execution 'for free', as well as async execution style similar to futures or promises. –  aplavin Mar 23 at 17:37
    
Have a look at Celluloid. –  britishtea Mar 23 at 19:31

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.