Timer statements
setTimeout / setInterval / clearTimeout / clearInterval — JS-shaped Timer sugar.
setTimeout(<callback>, <ms>) and
setInterval(<callback>, <ms>) are bare global
functions — not namespaced like taskManager.run(...) —
that generate and hide the full BrightScript Timer node
lifecycle (creation, field wiring, ObserveField, start,
cleanup) behind one call. <callback> can be an
anonymous function or a named function reference.
<script>
state ready: boolean = false
public function setup() {
setTimeout(function() {
state ready = true
}, 1500)
}
</script>Milliseconds, not seconds
True JS parity — setTimeout(fn, 1000) fires after 1 second.
Roku's own Timer.duration field is in seconds; the
conversion happens for you, at compile time when the duration is a literal.
setTimeout(fn, 1000) ' fires after 1 second — milliseconds, JS-shaped
setInterval(fn, 500) ' fires every 0.5 secondsHandles, setInterval, and clearing
Both calls return a handle — capture it (a plain local
or an m.<field>) to cancel later with
clearTimeout(<handle>)/clearInterval(<handle>).
A setInterval callback is called with no arguments, repeatedly,
until cleared.
<script>
public function setup() {
m.pollHandle = setInterval(onPoll, 500)
}
private function onPoll() {
state runningCount = taskManager.runningCount
}
private function stopPolling(key: string, press: boolean) {
if (press) {
clearInterval(m.pollHandle)
}
}
</script>Automatic cleanup on unmount
Every pending timer a component creates is automatically stopped
when that component's own node is removed — navigating away from a
router-mounted screen, an ancestor
{#if:destroy} tearing down a subtree that contains it,
or an {#each} block removing an item that contains
it — any nesting depth, in all three cases. You don't need to call
clearTimeout/clearInterval
yourself just to avoid a timer outliving its component — call them only when you want to cancel
early, while the component is still mounted.
This same automatic cleanup doesn't yet extend to taskManager's
own tracked tasks — a component's own timers are always cleaned up, but a task it started via
taskManager.run(...) keeps running/queued after that component is
destroyed unless you cancel it yourself. See task-manager's own "Not (yet) supported" list.
Reference implementation — apps/timers-demo
Every mechanism on this page has a router-mounted, scaled
chapter in apps/timers-demo — 3 chapters
(/basic-lifecycle through
/focus-teardown-ordering), reachable with REWIND/FAST-FORWARD
once compiled and sideloaded. Chapter 1 covers the one-shot/recurring basics, including
cancelling a pending setTimeout before it ever fires; chapter
2 proves the automatic unmount cleanup through a genuinely two-component-level-deep nested
cascade and an {#each} list-item removal; chapter 3
proves the ordering between focus-recovery and the ft_unmount
cascade for a synchronous (non-animated) teardown of a currently-focused, ticking widget. See
findings/timers-demo-app.md for what each chapter covers and
findings/demo-app-conventions.md for the app-structure convention it follows.
⚠️ Not (yet) supported
- ○
setTimeout/setIntervalmay appear only as a bare statement, or the entire right-hand side of a plain<local> =assignment — never nested inside a larger expression, a condition, or a loop header.clearTimeout/clearIntervalmay appear only as a bare statement. - ○ None of the four may be used inside a
derivedexpression, a dynamic template{expr}binding, or an{#if}/{#each}condition/collection/key expression — all recompute repeatedly, which would leak (or try to clear) a new Timer node every time. - ○ Not supported inside a .flsh class body — same reasons
taskManager.onAlertChanged/onResultaren't. - ○ The names
setTimeout/setInterval/clearTimeout/clearIntervalare reserved — they cannot be used as a field/derived/state/function/parameter name.
Exact grammar: GRAMMAR.md. Full feature status: docs/features.md.