These functions are for people who are looking for a more accurate task library. It includes delay, spawn and wait functions. In order to set it up, just copy this code and put in a ModuleScript.
local frame = task.wait() -- Counts the last frame delay
local task = {
wait = function(n)
local time_ = os.clock() -- Records the time when the function was called
n = tonumber(n) or 0 -- If it's not a number, it will count it as a zero (not recommended, less performance)
if os.clock() - time_ < n then -- If n has passed, then it doesn't yield.
if n > frame then -- Verifies if n is more than the last frame delay.
while os.clock() - time_ < n - frame do
frame = task.wait() -- Waits for the next frame until the time passed is close to n
end
end
while os.clock() - time_ < n do
if false then end -- Repeats this function that doesn't "yield" until n has passed.
end
end
return os.clock() - time_ -- Returns the time that has actually elapsed.
end,
spawn = function(func, ...)
local event = Instance.new("BindableEvent") -- Creates a BindableEvent
local param = {...} -- Puts the parameters in a table
if type(func) ~= "function" then
error("attempt to call a " .. typeof(func) .. " value")
end
event.Event:Connect(function() -- Connects the function with the BindableEvent
func(unpack(param)) -- Calls the function with the parameters
event:Destroy() -- Destroys the evet
end)
event:Fire() -- Fires the event
end
}
task.delay = function(n, func, ...)
local time_ = os.clock() -- Records the time when the function was called
local event = Instance.new("BindableEvent") -- Creates a BindableEvent
local param = {...} -- Puts the parameters in a table
n = tonumber(n) or 0 -- If it's not a number, it will count it as a zero (not recommended, less performance)
if type(func) ~= "function" then
error("attempt to call a " .. typeof(func) .. " value")
end
event.Event:Connect(function() -- Connects the function with the BindableEvent
if os.clock() - time_ < n then -- If n has passed, then it doesn't yield.
task.wait(n - os.clock() + time_) -- Waits n seconds.
end
func(unpack(param)) -- Calls the function with the parameters
event:Destroy() -- Destroys the event
end)
event:Fire() -- Fires the event
end
return task -- Returns the library
Just out of curiosity, is it even possible to make the engine wait that amount of time? I’ll imagine that even most basic code executions take a similar amount of time or even more than that.
That is not waiting though. Unless you call actual wait(), task.wait() or coroutine.yield() functions, you would achieve nothing but simply stall the entire engine for that duration.
I don’t see any practical applications where waiting less than a duration of the frame is necessary, since pretty much everything else in the engine happens once* in a single frame, which means even if you trigger something precisely at the exact moment you want to, you would see the result only after it renders.
* Physics run 4 times, one after another.
P.S.: There was a similar post a couple of months ago, and a member of Roblox Staff has explained why waiting more than 60 times per second is unnecessary:
I only call non-yieldable code when I want to wait an amount of seconds less than the duration of the previous frame. That’s why I use task.wait() before that.
if os.clock() - time_ < n then -- If n has passed, then it doesn't yield.
if n > frame then -- Verifies if n is more than the last frame delay.
while os.clock() - time_ < n - frame do
frame = task.wait() -- Waits for the next frame until the time passed is close to n
end
end
while os.clock() - time_ < n do
if false then end -- Repeats this function that doesn't "yield" until n has passed.
end
end
Yes, I can see that. However, I am more concerned about the practical applications of your library. Can you provide any example of where it would be useful to run something after precisely N seconds instead of just N +- delta_time seconds?
The only thing that would make it a tiny bit faster would be to set “clock” to a local variable, though I have no idea if it will make much of a difference.
These sorts of optimisations don’t apply to luau as roblox has a system called imports which localises all if not most of roblox’s standard libraries like os and the math library