1

How would I do a specific task every x amount of seconds in ruby? I've tried using Time.now.to_i for epoch then once a Time.now_i hits that task second it executes, but I have not successfuly done this, can someone show me a small example on how to execute a function every x amount of seconds?

Attempt:

def interval(timeout,function,*data)
     now = Time.now.to_i
     tasktime = Time.now.to_i + timeout
     taskfunction = function
     taskdata = data
end

I stopped the code there because I do not know how/what to do next in ruby, so what it should do for example if someone can generate a code that can do something like this example,

def say(word)
    puts word
end 

If you set a interval for the function would be say, the data would be the "word" then it would execute that function every x amount of seconds

anakin
  • 367
  • 3
  • 18

3 Answers3

2

You can use Kernel#sleep method for the same.

Here is the post

Community
  • 1
  • 1
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
  • With this method, can you execute other code while it's it does sleep? or will it stop executing all other code for x amount of seconds? I need it to be able to execute the ping, then continue to do other task while its waitng to send again will that do it? – anakin Sep 21 '13 at 21:34
  • 1
    The command "sleep" will block the ruby process. If you need to execute other code in the meantime, you could create a cronjob which handles the ping-call..or you can use a gem like whenever for that (https://github.com/javan/whenever) or you have to execute the code asynchronously with threads for example. – Matthias Sep 21 '13 at 21:38
2

If you simply sleep for a constant amount of time as suggested in other answers, the error will contaminate as it keeps running, and will not be accurate. In fact, each iteration would take longer than the given interval.

The answer shown below adjusts the lag each time per iteration.

module Kernel
  def tick_every sec, &pr
    Thread.new do loop do
      pr.call
      t = Time.now.to_f
      frac = t.modulo(sec.to_f)
      sleep(sec - frac)
    end end
  end
end

thread = tick_every(2) do
  puts "foo"
end
...
some_other_tasks
...
thread.kill
sawa
  • 165,429
  • 45
  • 277
  • 381
  • This method does work, but when I try to execute another function while it's doing this it does not execute until it finished executing "foo" is there another way to do it so it can putting that ever 2 seconds but executing other functions? – anakin Sep 21 '13 at 21:40
  • Put it in `Thread do ... end`. I actually has that at first, but took it off. I just put it back. – sawa Sep 21 '13 at 21:42
  • Hm, not sure if i'm doing right can you edit your answer show it shows me how to do multiple tasks while it's still doing the loop? But, other then that it works. – anakin Sep 21 '13 at 21:47
  • Sorry, use `kill` instead of `join`. – sawa Sep 21 '13 at 21:51
  • It appears this will not wait the correct amount of time between the first and second iterations of the loop (but should be good for all others). – ZX9 Apr 11 '18 at 02:58
1

This method would puts the word every 2 seconds endless, synchronously (means other ruby code has to wait until this execution is finished (..endless..:)).

def say(word)
  while true do
    t = Time.now.to_f
    puts word
    frac = t.modulo(2.to_f)
    sleep(2 - frac)
  end
end
Matthias
  • 4,355
  • 2
  • 25
  • 34
  • This does not iterate every 2 seconds. It would take more than that. You did not consider the time it takes to execute `puts word` and to go back to the beginning of the loop. – sawa Sep 21 '13 at 21:49
  • That´s true yes, I fixed it (extracted from your answer @sawa ;) ). – Matthias Sep 21 '13 at 21:59