User Story:
As a Roblox developer, it is currently too messy to implement a cancellable delay.
Use Case:
Take this case study as an example. We needed to interrupt the user’s sprint if they were sprinting for too long (exploiting or high latency), and we needed to start to regenerate their stamina one second after they had stopped sprinting.
The implementation of that functionality required us to keep track of the times events fired to prevent delayed code from running when its process had completed. With the ability to cancel delay(), we no longer need to do that.
Current Implementation:
local delays = {}
sprintRequestStart.OnServerEvent:Connect(function(invoker)
local maxSprintTime = getSprintTimeAllowed(invoker)
local start = tick()
markAllowedForSprint(invoker, start)
delays[invoker] = start
delay(maxSprintTime, function()
if delays[invoker] == start then
markDisallowedForSprint(invoker)
end
end)
end)
sprintRequestStop.OnServerEvent:Connect(function(invoker)
local start = tick()
markDisallowedForSprint(invoker)
markWaitingForRegen(invoker, start)
delays[invoker] = start
delay(regenCooldown, function()
if delays[invoker] == start then
markForRegen(invoker)
end
end)
end)
Ideal Implementation:
local delays = {}
sprintRequestStart.OnServerEvent:Connect(function(invoker)
if delays[invoker] then delays[invoker].cancel() end
local maxSprintTime = getSprintTimeAllowed(invoker)
markAllowedForSprint(invoker)
delays[invoker] = delay(maxSprintTime, function()
markDisallowedForSprint(invoker)
end)
end)
sprintRequestStop.OnServerEvent:Connect(function(invoker)
delays[invoker].cancel()
markDisallowedForSprint(invoker)
delays[invoker] = delay(regenCooldown, function()
markForRegen(invoker)
end)
end)
Benefits of Resolving:
If Roblox is able to address this issue, it would improve my development experience by allowing me to avoid the unnecessary mental overhead introduced by this awkward workaround. As seen in the second example, the ability to cancel a delay() results in a clear, logical flow that doesn’t obscure the true intention of the source.
Suggestions:
- Cancelling a delay that already finished should not error, much like disconnecting an already-disconnected event doesn’t error
- Using a generic cancel function i.e.
local d = delay(...) cancel(d)
would simplify the above code by not requiring me to check if there was an existing delay in the first event. It could also be used for a JS setInverval equivalent if one is ever implemented