Table of Contents

How To Use Callbacks

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.

Use A Function As A Callback

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.


Keep The Timer Object

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.


Use An Object Method As A 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.


Keep Callback State In One Object

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`.


Use Callbacks From Include Files

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.


Callback Arguments Must Match

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.


Callback Lifetime And Sessions

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.

Common Mistakes

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);

See Also


How-To index: How-To Guides