# Asynchronous execution

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

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)
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:
# ... 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?

• Are you aware of EventMachine? github.com/igrigorik/em-http-request/wiki/Issuing-Requests – tokland Mar 23 '15 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 '15 at 17:37
• Have a look at Celluloid. – britishtea Mar 23 '15 at 19:31