Skip to main content

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. TimerIds 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!");
}