Every now and then, I run into a situation where having something like this would be useful:
local intValue = Instance.new("IntValue")
intValue.Value = 0
-- Do something....
local result = intValue.Changed:Wait(30)
The value passed to the Wait
method is a timeout in seconds. If the timeout expires before the event fires, then nil is returned. Currently, the only real way to do this in LuaU is the following:
-- Performs the :Wait() functionality on an IntValue but has a timeout.
-- Checks the conditions on every frame.
local function intWait(intval: IntValue, timeout: number): number
-- Setup
local changed = false
local result = nil
local count = 0
-- Changed event on intval.
intval.Changed:Connect(function()
changed = true
result = intval.Value
end)
-- Wait for the value to change or the timeout to expire.
while count < timeout and changed == false do
count += task.wait()
end
-- Return
return result
end
Now the way that a wait function is usually implemented is that instead of running a loop to spin the task and increment a counter in user space, a system call is made to the kernel which sets the sleep timer property on the proc structure for the scheduler to handle. Each time the scheduler looks at it, it compares the timestamp from the sleep value to the system clock. If value > clock, then the wait is over and it proceeds with the context switch to that thread, unless it’s waiting for some other event.
Roblox’s wait()
and task.wait()
implementations are based on the engine video frame rate of either 1/30 or 1/60 respectively. Those numbers can differ on the client due to variable frame rates though. To get that kind of timing, the engine performs all tasks in the frame and then waits for the video refresh hardware interrupt to flip the rendered frame to the active display frame then proceeds to perform the full physics simulation and graphics rendering workflow for the next frame.
But to implement something like a :WaitForChild()
, you would check if the condition was true. If it is true, then you return the result, otherwise you yield the thread. You accumulate the time between yields until you reach the threshold and return a default value and print a warning. I figure that an implementation of :Wait(timeout)
would be something like a WaitForChild()
.
Basically, what I am looking for is the most efficient way to implement a :Wait(timeout)
with the tools that we have available to us. I would rather not burn CPU cycles in Lua to do this though. Is there a better way to implement this?
I did some looking around, but the main implementation that I found uses LoadLibrary()
which is long depreciated.
https://devforum.roblox.com/t/creating-an-efficient-timeout-for-wait/1032100
https://devforum.roblox.com/t/timeout-argument-for-eventwait/32737
https://devforum.roblox.com/t/waiting-event-for-a-certain-time/1572180
I have found an old feature request on this too that someone posted. The final suggestion was to add an Event:TimedWait(timeout)
method to resolve the issue of return values.