Timers
Overview
On ICP, canisters can set recurring timers that execute a piece of code after a specified period of time or regular interval. Times in Motoko are implemented using the Timer.mo
module, and return a TimerId
. TimerId
s are unique for each timer instance. A canister can contain multiple timers.
Example
A simple example is a periodic reminder that logs a new year's message:
import { print } = "mo:base/Debug";
import { abs } = "mo:base/Int";
import { now } = "mo:base/Time";
import { setTimer; recurringTimer } = "mo:base/Timer";
actor Reminder {
let solarYearSeconds = 356_925_216;
private func remind() : async () {
print("Happy New Year!");
};
ignore setTimer<system>(#seconds (solarYearSeconds - abs(now() / 1_000_000_000) % solarYearSeconds),
func () : async () {
ignore recurringTimer<system>(#seconds solarYearSeconds, remind);
await remind();
});
}
The underlying mechanism is a canister global timer that by default is issued with appropriate callbacks from a priority queue maintained by the Motoko runtime.
The timer mechanism can be disabled completely by passing the -no-timer
flag to moc
.
Low-level access
When lower-level access to the canister global timer is desired, an actor can elect to receive timer expiry messages by declaring a system
function named timer
. The function takes one argument used to reset the global timer and returns a future of unit type async ()
.
If the timer
system method is declared, the Timer.mo
base library module may not function correctly and should not be used.
The following example of a global timer expiration callback gets called immediately after the canister starts, i.e. after install, and periodically every twenty seconds thereafter:
system func timer(setGlobalTimer : Nat64 -> ()) : async () {
let next = Nat64.fromIntWrap(Time.now()) + 20_000_000_000;
setGlobalTimer(next); // absolute time in nanoseconds
print("Tick!");
}