Created
July 18, 2024 00:01
-
-
Save thadeu/6af4fbac5173ecce7600be072db68f80 to your computer and use it in GitHub Desktop.
Promise pattern using Ruby like JS
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
require 'pry' | |
require 'benchmark' | |
require 'httparty' | |
def http_get(url) | |
response = HTTParty.get(url) | |
# p response.status | |
if response.code != 200 | |
raise StandardError.new("http_get (#{url}) Error: #{response.code}") | |
end | |
response.parsed_response | |
end | |
def get_pages; http_get("https://random.dog/#{%w(woof).sample}.json"); end | |
def get_stats; http_get("https://baconipsum.com/api/?type=meat-and-filler"); end | |
class Result | |
attr_reader :value, :error | |
def initialize(value, error) | |
@value = value | |
@error = error | |
end | |
def Ok(output = nil, &block) | |
# return if !!@error | |
output.call(@value) if output.respond_to?(:call) | |
block.call(@value) if block_given? | |
end | |
def Err(output = nil, &block) | |
return if @error.nil? | |
output.call(@error) if output.respond_to?(:call) | |
block.call(@error) if block_given? | |
end | |
def on_success(&block) | |
return if !!@error | |
block.call(@value) | |
end | |
def on_error(&block) | |
return if @error.nil? | |
block.call(@error) | |
end | |
end | |
module Promise | |
extend self | |
# def all(promises, pool_size: 5) | |
# pool = Thread::Pool.new(pool_size) | |
# output = [] | |
# promises.each { |m| pool.process { output << m.call } } | |
# pool.shutdown | |
# output | |
# end | |
def all(promises, pool_size: 5, &block) | |
output = [] | |
err = nil | |
Thread.report_on_exception = false | |
promises.each_slice(pool_size) do |batch| | |
queue = Thread::Queue.new | |
batch.each do |functor| | |
queue << begin | |
Thread.new { functor.call } | |
end | |
end | |
queue << :done | |
loop do | |
job = queue.pop(true) | |
break if job == :done | |
output << job.value | |
end | |
queue.close | |
end | |
Thread.report_on_exception = true | |
output | |
rescue StandardError => e | |
file = e.backtrace[0] | |
func = e.backtrace[1] | |
err = [e.inspect, file, func].join(" ") | |
ensure | |
if block_given? | |
klass = Result.new(output, err) | |
klass.instance_eval(&block) | |
end | |
end | |
def all_settled(promises, pool_size: 5, &block) | |
output = [] | |
promises.each_slice(pool_size) do |batch| | |
queue = Thread::Queue.new | |
batch.each do |functor| | |
queue << Thread.new do | |
begin | |
{ status: :ok, value: functor.call } | |
rescue StandardError => e | |
{ status: :error, value: e.inspect, backtrace: e.backtrace } | |
end | |
end | |
end | |
queue << :done | |
loop do | |
job = queue.pop(true) | |
break if job == :done | |
output << job.value | |
end | |
queue.close | |
end | |
output | |
rescue StandardError => e | |
output << { status: :error, value: e } | |
ensure | |
if block_given? | |
oks = output.filter { |el| el[:status] == :ok }.map { |el| el[:value] } | |
errors = output.filter { |el| el[:status] == :error }.map { |el| el.slice(:value, :backtrace) } | |
klass = Result.new(oks, errors) | |
klass.instance_eval(&block) | |
end | |
end | |
end | |
arr = 5.times.map { :get_pages } | |
class Pages | |
def initialize | |
@lazy = false | |
end | |
def self.load_async | |
klass = new | |
klass.load_async | |
klass | |
end | |
def get_dogs(*) | |
!!@lazy ? proc { get_pages } : get_pages | |
end | |
def load_async | |
@lazy = true | |
self | |
end | |
end | |
module PromiseFormatter | |
def self.[](output) | |
output | |
end | |
def self.call(output) | |
output.each_with_index { |el, i| puts "\n#{i+1} #{el}" } | |
end | |
end | |
Benchmark.bm do |x| | |
x.report("with Promise.all\n") do | |
promises = arr.map { Pages.load_async.get_dogs('puppy') } | |
p Promise.all(promises) | |
end | |
x.report("with Promise.all_settled\n") do | |
# promises = arr.map { Pages.load_async.get_dogs('puppy') } | |
# Promise.all_settled(promises) { | |
# Ok { p [1, PromiseFormatter[_1]] } | |
# Err { p error } | |
# } | |
# Promise.all_settled(promises) { | |
# Ok -> v { p [1, PromiseFormatter[v]] } | |
# Err { p error } | |
# } | |
# Promise.all_settled(promises) { | |
# Ok { |v| p [1, PromiseFormatter[v]] } | |
# Err { p error } | |
# } | |
# Promise.all_settled(promises) { | |
# Ok PromiseFormatter | |
# Err { puts error } | |
# } | |
# Promise.all_settled(promises) do |result| | |
# p result.value | |
# end | |
# Promise.all_settled(promises) do |result| | |
# result.on_success do |value| | |
# p value | |
# end | |
# result.on_error do |error| | |
# p error | |
# end | |
# end | |
end | |
x.report("without promises\n") do | |
results = [] | |
# arr.each { results << Pages.new.get_dogs('puppy') } | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment