Hey there! I am trying to create a digital clock that keeps displaying the time. Here, the time is not fixed. It keeps changing like how it is in everyday-life.
There isn’t really an issue, rather I’ve got a query. In the code below, I have a bool value and whenever its value changes, the minute function runs. In the minute function, at the end, the bool value changes again. So as you can see, it’s now in an infinite loop and the time will keep changing. I can simply start the system by calling the initiate() function which changes the value of the bool. I wanted to know if this is a good practice or not. I tested this and it works absolutely fine, but will it cause any overflows or performance degradation if the server runs for too long? Are there any better options like while true do loops?
I’ve looked at some posts on Devforum where the main debate is whether loops are preferable or recursion, and I’ve got to know that recursion (though mine is not recursion) can cause stack-overflows, so loops are a better choice. Although, I also found a reply which said that event based systems can work well. So, this system which I have implemented, is it preferable over while true do loops or anything else?
Here’s my code:
--Variables for the infinite loop driver (bool value), the digital clock, and the text label
local bool = script.Parent.InfiniteLooper
local dc = workspace.LobbySciFi.ControlUnit2.DigitalClock
local time = dc.Adornee.DigitalClockGui.Frame.Time
--Maximum hours and minutes possible in a cycle
local maxHours = 23
local maxMinutes = 59
--Declare the current counters for number of hours and minutes
local hourCounter
local minuteCounter
--The time string to display on the label
local currentHour = ''
local currentMinute = ''
--Number of seconds in a minute
--Setting this to 2.5 will complete one whole cycle in one hour
local secondsPerMinute = 15
--The first function that runs when the server loads
function initiate()
--Assign a random value to both the counters to start off with a random time
hourCounter = math.random(0, maxHours)
minuteCounter = math.random(0, maxMinutes)
--Change the value of this bool to start counting
bool.Value = not bool.Value
end
--Reset the counters, restart the cycle
function reset()
hourCounter = 0
minuteCounter = 0
end
--Function that completes one minute
function runOneMinute(val)
--Check if the number of minutes passed are at maximum or more than maximum
if minuteCounter > maxMinutes then
--If yes, check if the number of hours passed is less than maxHours
if hourCounter < maxHours then
--If yes, increment the hourCounter by 1 and reset the minuteCounter
--This marks the completion of an hour
hourCounter += 1
minuteCounter = 0
else
--If hourCounter is more than or equal to maxHours, reset everything
reset()
end
end
--Convert the hours passed into a string
local hour_str = tostring(hourCounter)
--If the hours passed are less than 10, add a '0' in front of the string
if hourCounter < 10 then
currentHour = "0" .. hour_str
else
currentHour = hour_str
end
--Convert the minutes passed into a string
local minute_str = tostring(minuteCounter)
--If the minutes passed are less than 10, add a '0' in front of the string
if minuteCounter < 10 then
currentMinute = "0" .. minute_str
else
currentMinute = minute_str
end
--Concatenate both the counters and display them on the label
time.Text = currentHour .. ":" .. currentMinute
--Wait for a specific amount and then start the next minute
wait(secondsPerMinute)
minuteCounter += 1
--Change the bool value to keep running the cycle in an infinite loop
bool.Value = not val
end
--Whenever the value of the bool changes, run this function
bool.Changed:Connect(function(val)
--Call the minute function to make an infinite loop
runOneMinute(val)
end)
--Start the time system
initiate()
Looking forward to your replies, it would be great help indeed. Thank you so much for reading this!
This will not cause a stack overflow, I honestly think a regular While loop is better.
a stack overflow is caused because when a function calls itself it is still in memory and has to wait for itself self to stop calling.
after a function is played it will be removed from memory
when you changed the value of the bool the function is called independent from the other function.
I think a while loop is more faster to call so it would be just a bit less delayed than the bool value changing.
Thanks for clearing me about stack overflows. And yeah, I think you are right about while true do loop being faster to call than this event based system. So, will while true do loops cause no issues even though they are faster? Provided that there is a wait() every time it loops. Like will there be any yielding or anything?
Using any wait function will yield the current thread. Most of the time programmers substitute loops for RunService.Heartbeat because you can still keep track of the time, but it’s event based so you know exactly when your code will run in the task scheduler, and it does not yield, meaning you can run other code while this “loop” is running.
Okay but, in RunService.Heartbeat, even if you put a wait(n) it will keep running and wont consider that amount of time to yield. But, in this situation, I want it to wait for a couple of seconds before heading into the next minute. So, could you suggest me the best alternative to this or is this system I am using alright? Also, will a while true do loop be faster than what I did? (the event based system) Thanks.
You can easily modify a Heartbeat loop to only run at an interval by doing the following:
local timeInterval = 60 -- 1 minute
local timeCount = 0
game:GetService('RunService').Heartbeat:Connect(function(dt : number)
timeCount += dt
if timeCount >= timeInterval then
-- do whatever code you need it to do
foo()
-- reset the timer
timeCount = 0
end
end)
With Heartbeat, you track your time manually, without yields, which gives you a lot more control over your code.
What you are doing right now works if you aren’t creating any more lines of code afterwards (whether this code is good practice, I cannot say. Code usually depends on what situation you are in). If you are, you’re going to have to take into account the fact that you’re yielding the thread when you call wait.
Thanks! Also, according to you, could you rank these 3 in terms of performance, speed and accuracy?
→ The method which you are suggesting, which is RunService.Heartbeat
→ The method which I originally used (event based system)
→ While true do loops
Here, keep in mind that the wait is going to be quite long, about 15 seconds. So, what will be your final advice? Thanks once again. Have a good day.
I’ll replace that wait with task.wait (I know it wont change anything but it’s a better option). Also, I am not planning to add any more lines of code afterwards, not in this script. I will be creating other scripts though. So I think that should be fine. So out of what I have understood, while true do loops and this system which I’m using will yield the thread but RunService.Heartbeat wont, right? And hence, the latter is the best choice.
So, as of now, since I have no more code to write in this specific script, I can let that stay as it is. But in future, when I need more control, I’ll try using RunService.Heartbeat. Also, is a while true do loop faster than this system I am using, according to you? Apologies if I am asking too many questions, I just want to know it all. Thank you a lot though.
I wouldn’t say any particular method is ‘faster’ than the other at face value; it all depends on what your blocks of code are, and how long you’re waiting in between.
I would generally stay away from while true do loops. They constrain you just like using the wait function, where, if you want to add more code in that particular script, you’d have to terminate the loop before you do anything.