how to invoke a method for every second in ruby

Clone picture Clone · Sep 20, 2012 · Viewed 10.5k times · Source

I wanted to create a stopwatch program in ruby so I googled it and found this SO Q.

But over there, the author calls the tick function with 1000xxx.times. I wanted to know how I can do it using something like (every second).times or for each increment of second do call the tick function.

Answer

Wayne Conrad picture Wayne Conrad · Sep 21, 2012

This function:

def every_so_many_seconds(seconds)
  last_tick = Time.now
  loop do
    sleep 0.1
    if Time.now - last_tick >= seconds
      last_tick += seconds
      yield
    end
  end
end

When used like this:

every_so_many_seconds(1) do
  p Time.now
end

Results in this:

# => 2012-09-20 16:43:35 -0700
# => 2012-09-20 16:43:36 -0700
# => 2012-09-20 16:43:37 -0700

The trick is to sleep for less than a second. That helps to keep you from losing ticks. Note that you cannot guarantee you'll never lose a tick. That's because the operating system cannot guarantee that your unprivileged program gets processor time when it wants it.

Therefore, make sure your clock code does not depend on the block getting called every second. For example, this would be bad:

every_so_many_seconds(1) do
  @time += 1
  display_time(@time)
end

This would be fine:

every_so_many_seconds(1) do
  display_time(Time.now)
end