How to put a delay on a loop in Ruby? - ruby

For example, if I want to make a timer, how do I make a delay in the loop so it counts in seconds and do not just loop through it in a millisecond?

The 'comment' above is your answer, given the very simple direct question you have asked:
1.upto(5) do |n|
puts n
sleep 1 # second
end
It may be that you want to run a method periodically, without blocking the rest of your code. In this case, you want to use a Thread (and possibly create a mutex to ensure that two pieces of code are not attempting to modify the same data structure at the same time):
require 'thread'
items = []
one_at_a_time = Mutex.new
# Show the values every 5 seconds
Thread.new do
loop do
one_at_a_time.synchronize do
puts "Items are now: #{items.inspect}"
sleep 5
end
end
end
1000.times do
one_at_a_time.synchronize do
new_items = fetch_items_from_web
a.concat( new_items )
end
end

Somehow, many people think that putting a sleep method with a constant time interval as its argument will work. However, note that no method takes zero time. If you put sleep(1) within a loop, the cycle will surely be more than 1 second as long as you have some other content in the loop. What is worse, it does not always take the same time processing each iteration of a loop. Each cycle will take more than 1 second, with the error being random. As the loop keeps running, this error will contaminate and grow always toward positive. Especially if you want a timer, where the cycle is important, you do not want to do that.
The correct way to loop with constant specified time interval is to do it like this:
loop do
t = Time.now
#... content of the loop
sleep(t + 1 - Time.now)
end

Related

Performing action every few seconds, no matter what happens in running loop

I have a little problem. I want to iterate loop every time there is new line from socket, so I use this loop:
until #socket.eof? do
#something
end
And I also want to perform one action every 10 seconds, so I did something like this:
until #socket.eof? do
do_something
if time_last+10 < Time.now
time_last = Time.now
do_something_else
end
end
And my problem is that, that do_something_else action is performed only after there is a new line on socket. There is no problem if there is 100 lines / second. But if there is no new lines, nothing happens.
And if I get this whole if outside of until loop it woks only once on program start. Can I run two loops at once somehow?
You don't have to block on the socket. In this loop, it tries to read for 0.2 seconds and then checks if a task has to be launched. $guest is an array of sockets.
loop do
if task_time + 0.1 < now = Time.now.to_f
Task.execute(task_time = now)
end
next unless ready = select($guest, nil, nil, 0.2)
now = Time.now.to_f
for sock in ready[0]
// [...]
end
end
This is an extract of some legacy code of mine, but it sure works:
https://github.com/rosedomini/MystMUD/blob/master/MystMUD/myst/myst/mud.rb
You can schedule tasks, the best way is to use a scheduler.
You can do this using OpenWFEru and then:
scheduler.schedule_every('10s') { do_something }
See more in the link below:
https://www.igvita.com/2007/03/29/scheduling-tasks-in-ruby-rails/

How to call a method every X seconds in Rails? [duplicate]

For example, if I want to make a timer, how do I make a delay in the loop so it counts in seconds and do not just loop through it in a millisecond?
The 'comment' above is your answer, given the very simple direct question you have asked:
1.upto(5) do |n|
puts n
sleep 1 # second
end
It may be that you want to run a method periodically, without blocking the rest of your code. In this case, you want to use a Thread (and possibly create a mutex to ensure that two pieces of code are not attempting to modify the same data structure at the same time):
require 'thread'
items = []
one_at_a_time = Mutex.new
# Show the values every 5 seconds
Thread.new do
loop do
one_at_a_time.synchronize do
puts "Items are now: #{items.inspect}"
sleep 5
end
end
end
1000.times do
one_at_a_time.synchronize do
new_items = fetch_items_from_web
a.concat( new_items )
end
end
Somehow, many people think that putting a sleep method with a constant time interval as its argument will work. However, note that no method takes zero time. If you put sleep(1) within a loop, the cycle will surely be more than 1 second as long as you have some other content in the loop. What is worse, it does not always take the same time processing each iteration of a loop. Each cycle will take more than 1 second, with the error being random. As the loop keeps running, this error will contaminate and grow always toward positive. Especially if you want a timer, where the cycle is important, you do not want to do that.
The correct way to loop with constant specified time interval is to do it like this:
loop do
t = Time.now
#... content of the loop
sleep(t + 1 - Time.now)
end

Why does the first iteration of this not wait for user input from STDIN?

I'm finding this a bit odd. I would expect each iteration of parse_line(ARGF.read_line) here to wait for input from STDIN. What actually is happening is the first iteration skips waiting for user input, but subsequent iterations do wait. My temporary workaround is just to iterate an extra time.
def parse_line(line)
line.split(" ").map(&:to_i)
end
def get_number_of_slices
parse_line(ARGF.readline(1)).first
end
def get_all_slice_dimensions(number_of_slices)
number_of_slices += 1 # for some reason the first read doesn't wait for user input...
number_of_slices.times.collect { puts "iter"; parse_line(ARGF.readline) }
end
def main
puts (get_all_slice_dimensions get_number_of_slices)
end
main
Added code so that it is a full working program. I found that when I changed readline(1) to readline in get_number_of_slices, I did not need to add 1 to number_of_slices in get_all_slice_dimensions any longer, but I would still like to know the reason why this affects the behavior. If you pay attention to the output, iter will print twice the first time.

ping function every x amount seconds

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
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
You can use Kernel#sleep method for the same.
Here is the post
Ruby sleep or delay less than a second?
Tell Ruby Program to Wait some amount of time
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

Is there a way to write a Ruby loop that runs one iteration for a maximum set amount of time?

I'm looking to create a Ruby (MRI 1.9.3) loop that runs at most for a certain amount of time, and once that time is up it goes to the next iteration of the loop.
For example, this is what I'm hoping to achieve:
timer = Timer.new
while foo
timer.after 5 do # The loop on foo only gets to run for 5 seconds
next
end
# Do some work here
end
So far, I've found tarcieri's gem called Timers (https://github.com/tarcieri/timers) which is what I'm trying to emulate in the code above, but my implementation doesn't give the behavior I expect, which is for the loop to go to the next iteration after 5 seconds if my work takes longer than that. Any ideas?
require 'timeout'
timeout_in_seconds = 5
while foo
begin
Timeout.timeout(timeout_in_seconds) do
# Do some work here
end
rescue Timeout::Error
next
end
end
It's been awhile since I brushed off my Ruby skills, but I believe you can do this with the timeout library.
require 'timeout'
while foo
Timeout.timeout(5) do
work()
end
end
You can also try this:
time = 60
time_start = Time.now
begin
time_running = Time.now - time_start
#You_code_goes_here
end until (time_running.to_i >= time)
That loop will happen until the time_running var is equal or greater than "60".

Resources