Skip to content
Engine

Async and Events

Timer

Delayed and repeating tasks, stopwatches and countdowns that can be paused and run on any clock.

PackageShared

Getting it

local Timer = Engine:GetPackage("Timer")

Overview

Timer gives you three tools, all driven by RunService.Heartbeat:

  • Tasks from Timer.After and Timer.Every: run a function once after a delay, or repeatedly.
  • Stopwatch: measure how much time has passed, with laps.
  • Countdown: count down from a duration and fire signals on every tick and at zero.

Every constructor takes an optional clock function. By default it is os.clock, so time is real time. Pass a different clock and the timer follows it instead. TimeService does exactly this to make timers that speed up, slow down and pause with game time.

local Timer = Engine:GetPackage("Timer")

local reminder = Timer.After(30, function()
	print("30 seconds passed")
end)

local autosave = Timer.Every(60, function()
	saveEverything()
end)

-- later
reminder:Cancel()
autosave.Paused = true

Tasks

After

Timer.After(t: number, fn: () -> (), clock: Clock?): Task

Calls fn once after t seconds, then cancels itself.

Every

Timer.Every(t: number, fn: () -> (), clock: Clock?): Task

Calls fn every t seconds. If a frame takes longer than t, fn runs as many times as needed to catch up.

Task

MemberDescription
Cancelled: booleanRead-only. true once the task is cancelled or an After task has run.
Paused: booleanRead or set. A paused task does not count time.
:Cancel()Stops the task for good.
:Pause() / :Resume()Same as setting Paused.

Stopwatch

Stopwatch

Timer.Stopwatch(clock: Clock?): Stopwatch

Creates a stopped stopwatch at zero.

local stopwatch = Timer.Stopwatch()
stopwatch:Start()
-- ... the player runs the course ...
local lapTime = stopwatch:Lap()
local total = stopwatch:Stop()
print(string.format("Lap %.2fs, total %.2fs", lapTime, total))
MemberDescription
IsRunning: booleanRead-only.
Elapsed: numberRead-only. Seconds counted so far, including the running stretch.
:Start()Starts counting. Does nothing if it is already running. Also starts a new lap.
:Stop(): numberStops counting and returns the total elapsed time.
:Pause() / :Resume()Aliases of :Stop() and :Start().
:Reset()Stops and goes back to zero.
:Lap(): numberReturns the time since the last lap (or since :Start()) and starts a new lap.

Countdown

Countdown

Timer.Countdown(t: number, clock: Clock?): Countdown

Creates a countdown of t seconds. It does not run until you call :Start().

local roundTimer = Timer.Countdown(120)

roundTimer.Tick:Connect(function(remaining)
	label.Text = string.format("%d", math.ceil(remaining))
end)

roundTimer.Finished:Connect(function()
	endRound()
end)

roundTimer:Start()
MemberDescription
IsRunning: booleanRead-only.
Remaining: numberSeconds left. You can set it; it never goes below 0.
Tick: Signal<number>Fires every frame while running with the seconds left, and once with 0 at the end.
Finished: Signal<>Fires when the countdown reaches zero.
:Start()Starts or continues counting down. If nothing is left, fires Tick(0) and Finished straight away.
:Pause() / :Cancel()Stop counting down. Remaining keeps its value, so :Start() or :Resume() continues.
:Resume()Alias of :Start().
:Reset(t: number?)Stops and sets Remaining back to the duration, or to a new duration t.
:AddTime(delta: number)Adds (or with a negative number removes) time.

Types

export type Clock = () -> number
export type Task
export type Stopwatch
export type Countdown