Use `timer(callback)` when Expr code needs to run repeatedly after a time interval.
Timers are useful for polling status, updating a dialog, checking a serial device, or running a short periodic task. They are also easy to misuse because the callback runs later, outside the original expression flow.
For learning and testing, use `start(intervalMs, count)` so the timer stops by itself.
function Tick(count) { print('tick ', count); } my_timer = timer(Tick); my_timer.start(1000, 5); // five callbacks, one second apart
The callback receives one argument: the timer call count. The first callback receives `1`.
A limited timer is safer than a continuous timer while you are testing because it cannot keep running forever by accident.
`start(intervalMs)` starts a continuous timer.
function Tick(count) { print('tick ', count); } my_timer = timer(Tick); my_timer.start(1000);
Keep the timer object in a variable so you can stop it later.
if (my_timer.is_running()) { my_timer.stop(); }
Avoid this when you need later control:
timer(Tick).start(1000); // hard to stop or inspect later
A timer callback should do a small amount of work and return.
Good callback work:
Avoid slow or blocking work in a timer callback:
function Tick(count) { // Avoid long loops, blocking dialogs, or slow file work here. print('tick ', count); }
If a task is large, split it into small steps and do one step per timer call.
For anything more than a simple test, keep timer state in an object.
class Monitor() { ticks = 0; active = true; function Tick(count) { ticks = count; if (!active) { return; } print('monitor tick ', ticks); } } monitor = Monitor(); monitor_timer = timer(monitor.Tick); monitor_timer.start(1000);
This keeps the callback and its state together. It also avoids many root variables such as `monitor_ticks`, `monitor_active`, and `monitor_last_value`.
Prefer stopping a timer from normal script code, a dialog button, or a cleanup step.
function Tick(count) { print('tick ', count); } my_timer = timer(Tick); my_timer.start(1000); // later my_timer.stop();
For a known number of callbacks, use `start(intervalMs, count)` instead of stopping from inside the callback.
my_timer = timer(Tick); my_timer.start(500, 10); // stops after ten callbacks
This keeps the timer lifecycle clear.
A timer can update dialog state while the dialog is open.
Store the window, controls, and timer in one object.
class StatusDialog() { wnd = none(); status_label = none(); update_timer = none(); function Show() { status_label = label() .position(20, 20) .size(200, 24) .text('Waiting'); wnd = window() .title('Status') .size(260, 110) .buttons(false, false, true) .on_closed(this.OnClosed); wnd.add(status_label); wnd.show(); update_timer = timer(this.Update); update_timer.start(1000); } function Update(count) { status_label.text('Tick ' + count.to_string()); } function OnClosed(code) { if (!update_timer.is_none() && update_timer.is_running()) { update_timer.stop(); } } } dlg = StatusDialog(); dlg.Show();
The `on_closed` callback stops the timer when the window closes.
A timer remembers the session that was active when the timer was created. The callback runs in that session.
If that session is deleted while the timer is still running, the timer stops.
Do not create a timer in a temporary session and then delete that session while the timer is still needed.
old_session = session.current; job_session = 'timer_job'; session.create(job_session); session.set_variable(job_session, 'old_session', old_session); session.join(job_session); function Tick(count) { print('tick ', count); } job_timer = timer(Tick); job_timer.start(1000, 3); // Wait for the timer to finish, or stop it, before deleting this session. session.join(old_session); session.delete(job_session);
For long-lived timers, create them in a long-lived session and keep their objects there.
`timer` objects are not cloneable. They own active timer state and a callback reference.
Do not expect to move a timer object to another session with `session.set_variable(…)`.
Instead, create and stop the timer in the session that owns the callback.
function Tick(count) { print('tick ', count); } my_timer = timer(Tick); my_timer.start(1000);
Keep normal data, such as counters and settings, in cloneable values or object fields. Keep the live timer object local to its owning session.
If a timer callback raises an error, the timer prints a timer callback error message and stops.
Validate values inside the callback before using them.
class Poller() { divisor = 1; function Tick(count) { if (divisor == 0) { print('invalid divisor'); return; } value = count / divisor; print(value); } } poller = Poller(); my_timer = timer(poller.Tick); my_timer.start(1000);
Use `print()` while developing timer callbacks. It is easier to see why a timer stopped.
A very small interval can create unnecessary load, especially if the callback does host work.
Use an interval that matches the job:
Avoid using a timer as a tight loop. If you need a calculation loop, use normal loop syntax in one expression. If you need periodic host interaction, use a timer with a practical interval.
Calling the callback instead of passing it:
my_timer = timer(Tick()); // wrong my_timer = timer(Tick); // correct
Creating a continuous timer during testing:
my_timer.start(1000); // runs until stopped my_timer.start(1000, 5); // safer while testing
Deleting the timer session while the timer is still needed:
// Stop the timer before deleting the session that owns it. my_timer.stop();
Doing too much work in the callback:
function Tick(count) { // Keep this short. }
Use a class for long-lived timers:
class TimerJob() { t = none(); count = 0; function Start() { t = timer(this.Tick); t.start(1000); } function Stop() { if (!t.is_none() && t.is_running()) { t.stop(); } } function Tick(timer_count) { count = timer_count; print('tick ', count); } } job = TimerJob(); job.Start(); // later job.Stop();
This keeps the timer, callback, and state in one object.