A callback is code that you give to another object so it can call you later.
Callbacks are used by timers, dialogs, GUI controls, Python tasks, serial communication, and other host objects. The exact arguments depend on the object, but the basic pattern is always the same: define a function or method, then pass it without calling it.
Define a function with the arguments expected by the object.
For `timer`, the callback receives one argument: the timer call count. The first call receives `1`.
function OnTimer(count) { print('tick ', count); } my_timer = timer(OnTimer); my_timer.start(1000, 3);
This calls `OnTimer` three times, once per second.
The important detail is that `OnTimer` is passed without parentheses.
my_timer = timer(OnTimer); // correct
This is not the same:
my_timer = timer(OnTimer()); // wrong
`OnTimer()` calls the function immediately. `OnTimer` passes the function as a callback.
Store the timer object in a variable if you want to stop it or check whether it is still running.
function Tick(count) { print('tick ', count); } my_timer = timer(Tick); my_timer.start(500); // later if (my_timer.is_running()) { my_timer.stop(); }
A timer can run continuously with `start(intervalMs)`, or a fixed number of times with `start(intervalMs, count)`.
my_timer = timer(Tick); my_timer.start(250, 10); // ten callbacks
For a fixed number of callbacks, prefer `start(intervalMs, count)` instead of stopping the timer from inside its own callback.
Callbacks often need state. A class is a good place to keep that state.
class Counter() { count = 0; function Tick(timer_count) { count += 1; print('timer ', timer_count, ', object count ', count); } } counter = Counter(); my_timer = timer(counter.Tick); my_timer.start(1000, 5);
The callback is `counter.Tick`, not `counter.Tick()`.
The timer passes its call count to the method. The object keeps its own `count` field between calls.
For larger scripts, keep the callback function and the data it changes in the same object.
class Monitor() { ticks = 0; last_message = ''; function OnTimer(timer_count) { ticks = timer_count; last_message = 'tick ' + timer_count.to_string(); print(last_message); } } monitor = Monitor(); monitor_timer = timer(monitor.OnTimer); monitor_timer.start(1000);
This avoids many separate root variables such as `monitor_ticks`, `monitor_message`, and `monitor_timer_count`.
Include files can define classes with callback methods.
Example include file:
// monitor.expr class Monitor() { ticks = 0; function OnTimer(count) { ticks = count; print('tick ', ticks); } }
Using it:
include "monitor.expr" as Mon monitor = Mon::Monitor(); monitor_timer = timer(monitor.OnTimer); monitor_timer.start(1000, 5);
This is a useful pattern for reusable timer, dialog, serial, or GUI logic.
The callback must accept the arguments that the object sends.
For `timer`, use one parameter:
function Tick(count) { print(count); }
If the callback expects the wrong number of arguments, the callback raises an error. For a timer, callback errors are printed and the timer stops.
A callback runs later, after the current expression has finished.
For timer callbacks:
Keep session cleanup in mind when using timers from temporary job sessions.
function Tick(count) { print('tick ', count); } my_timer = timer(Tick); my_timer.start(1000); // Do not delete the session that owns Tick while this timer is still needed.
Calling the callback instead of passing it:
my_timer = timer(Tick()); // wrong my_timer = timer(Tick); // correct
Using the wrong number of callback arguments:
function Tick() { print('tick'); // wrong for timer: timer sends count }
Forgetting to keep the timer object when you need to stop it later:
timer(Tick).start(1000); // hard to stop later my_timer = timer(Tick); // better my_timer.start(1000);
Putting too much work in a callback:
function Tick(count) { // Avoid slow loops or blocking work here. print('tick ', count); }
Keep callbacks short. Let them update state, send a small command, or schedule the next step.
For simple callbacks, use a function:
function Tick(count) { print('tick ', count); } my_timer = timer(Tick); my_timer.start(1000, 5);
For callbacks with state, use an object method:
class JobState() { ticks = 0; function Tick(count) { ticks = count; } } job = JobState(); my_timer = timer(job.Tick); my_timer.start(1000);