Open Asterune
Timing and loops

Timing and loops

Schedule tasks, measure elapsed time, and handle frame updates.

Use task for scheduled work and RunService for frame updates. Scheduling delays are minimum waits rather than exact execution times.

Wait and measure elapsed time

task.wait(n) yields until a frame at or after the requested delay, in seconds. Without an argument, it waits one frame. Repeated waits can accumulate delay. Compare a clock against a deadline for time-based conditions.

Luau
local deadline = workspace.DistributedGameTime + 30
while workspace.DistributedGameTime < deadline do
  task.wait(0.25)
end
print("time is up")

DistributedGameTime measures seconds since the place started. The code checks the deadline on each iteration, so completion still occurs on a scheduled frame.

Frame updates

RunService exposes update signals:

SignalTiming
PreSimulationBefore physics
PostSimulationAfter physics
PreAnimationBefore animation evaluation
PreRenderBefore rendering

Callbacks receive elapsed time in seconds. Multiply rates by this value so updates do not depend on frame count. Keep render callbacks short because they run before drawing.

BindRender takes a numeric priority and callback, then returns a RenderBinding. Call its Disconnect method to remove the callback.

Schedule work

task.spawn starts a function on a separate thread. task.defer schedules it after the current scheduler pass. task.delay schedules it after a minimum delay. Errors in scheduled callbacks are reported separately from the calling thread.

Luau
local pending = task.delay(5, function()
  print("delayed callback")
end)
task.cancel(pending)

task.cancel cancels a deferred or delayed thread before it runs. This example cancels the callback immediately.

Schedule destruction

Debris schedules part destruction without a waiting thread per part. Its queue is checked periodically, so removal timing is approximate. Other instance types are currently ignored.

Luau
local temporary = Instance.new("Part")
temporary.Parent = workspace
game:GetService("Debris"):AddItem(temporary, 2)

Repeating work

Loops must yield so other scripts and engine updates can run. Give long-running loops a stop condition, or retain an event connection that can be disconnected. Use a tween for interpolation between fixed property values.