dry-rb / dry-effects

Algebraic effects in Ruby
https://dry-rb.org/gems/dry-effects/
MIT License
112 stars 20 forks source link

Add timeout effect #59

Closed flash-gordon closed 5 years ago

flash-gordon commented 5 years ago

Another perfect example of using effects:

class MakeRequest
  include HTTParty
  include Dry::Monads[:result, :try]
  include Dry::Effects.Timeout(:http)

  def call(url)
    if timed_out?
      Failure(:timeout)
    else
      Try[Net::ReadTimeout, Net::OpenTimeout, Net::WriteTimeout] {
        self.class.get(url, timeout: timeout)
      }.to_result.discard
    end
  end
end

request = MakeRequest.new
with_timeout = Object.new.extend(Dry::Effects::Handler.Timeout(:http, as: :call))

with_timeout.(1.0) { Array.new(5) { make_request.('http://httpstat.us/200?sleep=300') } }
# => => [Success(), Failure(#<Net::ReadTimeout: Net::ReadTimeout>), Failure(:timeout), Failure(:timeout), Failure(:timeout)]

It's already cool since it decouples time-to-process of your application from the intermediate code you have. But we can make even cooler by running it in parallel:

with_parallel = Object.new.extend(Dry::Effects::Handler.Parallel(as: :call))

extend Dry::Effects.Parallel

with_timeout.(1.0) do
  with_parallel.() do
    join(Array.new(5) { par { make_request.('http://httpstat.us/200?sleep=300') } } )
  end
end
# => [Success(), Success(), Success(), Success(), Success()]

đŸ‘† You can see how timeout is shared across different "threads". It requires 0 glue code on the dry-effects side.