Last active
December 10, 2015 01:09
-
-
Save kirel/4356686 to your computer and use it in GitHub Desktop.
Because I wanted to play with Threads...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module Deferred | |
| class Proxy < BasicObject | |
| def initialize(deferred, callback, errback) | |
| @deferred, @callback, @errback = deferred, callback, errback | |
| end | |
| def method_missing(name, *args, &block) | |
| ::Thread.new do | |
| begin | |
| @callback.call(@deferred.send(name, *args, &block)) | |
| rescue => e | |
| @errback.call(e) | |
| end | |
| end | |
| end | |
| end | |
| # returns an object where all methods calls are async and return to either callback or errback | |
| def deferred(callback = proc {}, errback = proc {}) | |
| Proxy.new(self, callback, errback) | |
| end | |
| end | |
| module Synchronized | |
| class Proxy < BasicObject | |
| def initialize synchronized | |
| @synchronized = synchronized | |
| end | |
| def method_missing(name, *args, &block) | |
| @return, @err = nil | |
| callback = ::Kernel.lambda { |ret| @return = ret } | |
| errback = ::Kernel.lambda { |err| @err = err } | |
| @synchronized.send(name, *args, callback, errback, &block).join | |
| raise @err if @err | |
| return @return | |
| end | |
| end | |
| # returns an object on which async methods can be called like sync methods (as long as the async method returns the thread running the async method) | |
| def synchronized | |
| Proxy.new(self) | |
| end | |
| end | |
| module Future | |
| class Promise | |
| include Deferred | |
| def initialize &calculation | |
| @calculation = calculation | |
| @return, @err = nil | |
| callback = lambda { |ret| @return = ret } | |
| errback = lambda { |err| @err = err } | |
| @deferred = deferred(callback, errback).call | |
| end | |
| def value | |
| @deferred.join | |
| raise @err if @err | |
| return @return | |
| end | |
| def call | |
| @calculation.call | |
| end | |
| end | |
| class Proxy < BasicObject | |
| def initialize target | |
| @target = target | |
| end | |
| def method_missing(name, *args, &block) | |
| Promise.new do | |
| @target.send(name, *args, &block) | |
| end | |
| end | |
| end | |
| # returns an object where all method calls return promises that can be forced when calling value on it | |
| def future | |
| Proxy.new(self) | |
| end | |
| end | |
| class Obj | |
| include Deferred | |
| include Synchronized | |
| include Future | |
| def call arg | |
| sleep 1 | |
| puts "#{Thread.current} slept 1s" | |
| sleep 1 | |
| puts "#{Thread.current} slept 2s" | |
| sleep 1 | |
| return "#{Thread.current} #{arg}" | |
| end | |
| def panic | |
| raise 'panic' | |
| end | |
| def async_call(arg, callback, errback) | |
| deferred(callback, errback).call(arg) | |
| end | |
| def async_panic(callback, errback) | |
| deferred(callback, errback).panic | |
| end | |
| def sync_call(arg) | |
| synchronized.async_call(arg) | |
| end | |
| def future_call(arg) | |
| future.call(arg) | |
| end | |
| def future_panic | |
| future.panic | |
| end | |
| end | |
| # Tests | |
| callback = lambda { |arg| puts "callback: #{arg}" } | |
| errback = lambda { |arg| puts "errback: #{arg}" } | |
| promise = Obj.new.future_call('fin') # runs in background | |
| Obj.new.async_call('fin', callback, errback) # runs in background and calls callback when finished | |
| Obj.new.async_panic(callback, errback) # runs in background and calls errback | |
| puts "synchronized #{Obj.new.sync_call('fin')}" # blocks and returns 'fin' | |
| puts "promise: #{promise.value}" # blocks until promise is done and returns 'fin' | |
| puts "promise: #{Obj.new.future_panic.value}" # raises panic |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment