LEVIATHAN v962456e · 962456eee1

Standard Library

class Timer

A source of ticks on the event loop.

since 0.1.0-alpha.1linuxwindows

Overview

A timer is a stream: the event loop pushes tick numbers 1, 2, 3, ... into it, so subscribe and for loops over ticks() work as with any other stream. Create one with after for a single tick or every for a repeating one, or with the constructor for a separate first delay and interval. The program keeps running while a timer is live; a one-shot timer releases itself after it fires, and cancel releases a repeating timer.

Callbacks run one at a time on the loop thread, never at the same moment.

Description

A timer is a stream. The event loop pushes a tick number into it each time the timer fires, counting from 1, so all the ordinary stream tools (subscribe, pull, for ... in) work on it.

You create timers with two functions in std:

  • std::after(ms) is a one-shot timer: it fires once, ms milliseconds from now, and then releases itself.
  • std::every(ms) is a repeating timer: it fires every ms milliseconds until you cancel it.

A Timer has three members. ticks() returns an InStream<int> over the tick numbers, subscribe(callback) is shorthand for subscribing to those ticks, and cancel() stops the timer and releases the work it holds.

A timer that is waiting to fire keeps the program alive (see lang.event-loop). That is why a repeating timer needs a cancel(): without one, the program never exits.

Reading ticks, then counting them

Timer t = std::every(10);
InStream<int> ticks = t.ticks();
int total = 0;
for (int n in ticks) {
    total += n;
    if (n == 4) { t.cancel(); ticks.close(); }
}
console.writeln("sum of ticks: ${total}");
Timer one = std::after(5);
one.subscribe((n) => console.writeln("tick number is ${n}"));
sum of ticks: 10
tick number is 1

Rules

  • Tick numbers start at 1 and increase by one each firing.
  • A one-shot timer fires exactly once, so its only tick is 1.
  • cancel() stops a timer; a one-shot releases itself after firing and needs no cancel.
  • Timers share the stream rules: one consumer, so subscribe and for ... in over the same timer are mutually exclusive. To end a for loop over a repeating timer, call cancel() and close the stream, as the example does.
  • Timers due at the same moment fire in the order they were created.
  • A timer callback runs on the event loop and never concurrently with other code. An uncaught exception in it stops the loop and ends the program with status 1.

Examples

Three timers created out of order still fire in due-time order:

One-shot timers fire when due

Timer slow = std::after(30);
Timer fast = std::after(10);
Timer medium = std::after(20);
slow.subscribe((n) => console.writeln("slow"));
fast.subscribe((n) => console.writeln("fast"));
medium.subscribe((n) => console.writeln("medium"));
fast
medium
slow

Notes

Bare std::sysTimerStart(delayMs, intervalMs, callback) is the public low-level form the two functions are built on; see lang.system-floor. Prefer std::after and std::every, because they give you the Timer to cancel.

Examples

A repeating timer that cancels itself after three ticks

Timer t = Timer(5, 5);
t.subscribe((n) => {
    console.writeln("tick ${n}");
    if (n == 3) {
        t.cancel();
        console.writeln("cancelled");
    }
});
tick 1
tick 2
tick 3
cancelled

Constructors

new

new(int delayMs, int intervalMs)

Start a timer.

The first tick comes after delayMs milliseconds. A positive intervalMs makes the timer repeat at that interval; 0 makes it fire once.

Parameters

delayMs
Milliseconds until the first tick.
intervalMs
Milliseconds between later ticks; 0 for a single tick.

Examples

Timer once = Timer(5, 0);
once.subscribe((n) => { console.writeln("fired, tick ${n}"); });
console.writeln("timer started");
timer started
fired, tick 1

Methods

cancel

cancel() -> void

Stop the timer.

No further ticks are delivered, and a repeating timer stops keeping the program alive. Cancelling a timer that already fired once, or one that was already cancelled, does nothing.

Examples

Timer t = std::every(5);
t.subscribe((n) => {
    console.writeln("tick ${n}");
    if (n == 2) {
        t.cancel();
        console.writeln("stopped");
    }
});
tick 1
tick 2
stopped

subscribe

subscribe((int) => void cb) -> void

Call a function on every tick.

The callback receives the tick number. This claims the timer's stream, so it cannot be combined with ticks.

Parameters

cb
The function to call with each tick number.

Examples

Timer t = std::every(5);
t.subscribe((n) => {
    console.writeln("tick ${n}");
    if (n == 2) { t.cancel(); }
});
tick 1
tick 2

ticks

ticks() -> InStream<int>

Get the timer's ticks as a stream.

Each tick is the number of the tick, counting from 1. Iterating the stream waits for each tick as it arrives. The stream is claimed by the first reader, so use either ticks or subscribe, not both.

Returns

A stream of tick numbers.

Examples

Timer rep = std::every(5);
for (int tick in rep.ticks()) {
    console.writeln("tick ${tick}");
    if (tick == 3) { break; }
}
rep.cancel();
console.writeln("done");
tick 1
tick 2
tick 3
done

See also

  • after — Start a timer that fires once.
  • every — Start a repeating timer.
  • InStream — The reading end of a stream: a queue of values of type T that something else produces.
  • Promise — A value that arrives later.