Skip to content

Instantly share code, notes, and snippets.

@thadeu
Created July 18, 2024 00:01
Show Gist options
  • Save thadeu/6af4fbac5173ecce7600be072db68f80 to your computer and use it in GitHub Desktop.
Save thadeu/6af4fbac5173ecce7600be072db68f80 to your computer and use it in GitHub Desktop.
Promise pattern using Ruby like JS
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