time

js/globals/time.ts

Timer globals and the performance object.

Implements the web-standard timer API: - setTimeout(fn, ms, ...args) → integer id - clearTimeout(id) - setInterval(fn, ms, ...args) → integer id - clearInterval(id) - setImmediate(fn, ...args) → integer id - clearImmediate(id) - queueMicrotask(fn) - performance.now() → milliseconds (float, monotonic)

All timer functions are automatically installed on globalThis before user scripts run, so no import is needed:

setTimeout(() => console.log('hi'), 500);
setImmediate(() => console.log('after this turn'));

Loop integration

Timers are scheduled via internal:runtime/loop, which is a singleton. Pending timers keep the process alive until they fire: the process exits only once all pending timers have fired (or been cancelled) and the script is otherwise done.

Cancellation

clearTimeout / clearInterval mark the timer as cancelled and remove it from the event loop immediately, so a cancelled timer no longer keeps the process alive. All timer APIs share one numeric id space, so ids from setTimeout, setInterval, and setImmediate may be passed to any of the clear functions interchangeably. Clearing an unknown or already-fired id is a silent no-op.

performance.now()

Returns the elapsed time in milliseconds since module load, with sub- millisecond precision. Uses the same high-resolution monotonic clock as fino:bench: - macOS: mach_continuous_time() (advances during sleep) - Linux: clock_gettime(CLOCK_MONOTONIC)

This is intentionally a small Performance subset. Fino exposes EventTarget dispatch plus now(), timeOrigin, and toJSON(); it does not implement PerformanceEntry, mark(), measure(), observers, or a performance timeline.

Learn more:

Classes

class Performance extends EventTarget {

Subset of the web Performance API, exposed as the Performance global.

Instances provide:

  • now() — monotonic milliseconds elapsed since module load (float)
  • timeOrigin — Unix timestamp (ms) captured at module load
  • toJSON() — JSON-serializable snapshot
  • EventTarget methods (addEventListener, dispatchEvent, ...) inherited from the EventTarget global implementation

PerformanceEntry, mark(), measure(), observers, and the performance timeline are not part of this runtime subset.

The class exists so performance instanceof Performance and WebIDL-style brand checks behave as on the web; it cannot be constructed directly. Calling now(), toJSON(), or the timeOrigin getter with a this that is not a genuine Performance instance throws a TypeError (Illegal invocation).

console.log(performance instanceof Performance); // true
const start = performance.now();
// ... work ...
console.log(`took ${performance.now() - start}ms`);

Getters

get timeOrigin()

Unix timestamp in milliseconds captured when this module loaded.

Combine with now() to convert a monotonic reading into an approximate wall-clock time: timeOrigin + now(). Throws a TypeError if read from a this that is not a Performance instance.

performance.timeOrigin <= Date.now(); // true

Constructors

constructor(token?: object)

Not user-constructible: throws a TypeError (Illegal constructor) unless invoked with the module-private token. Use the shared performance instance instead.

Methods

now()

Return monotonic milliseconds elapsed since module load, with sub-millisecond precision.

The clock is not affected by system clock adjustments, so it is suitable for measuring durations — not for wall-clock timestamps (use Date.now() or timeOrigin + now() for those). Throws a TypeError if called on a this that is not a Performance instance.

const start = performance.now();
const elapsed = performance.now() - start;
toJSON()

Return a JSON-serializable performance snapshot.

Only timeOrigin is included in this subset. Throws a TypeError if called on a this that is not a Performance instance.

JSON.stringify(performance.toJSON()); // {"timeOrigin":...}

Constants

const performance

The shared Performance instance, installed as globalThis.performance.

This is the only Performance instance in a realm — the constructor is not user-callable. Its timeOrigin marks when the runtime loaded this module.

const start = performance.now();
await new Promise((resolve) => setTimeout(resolve, 50));
console.log(performance.now() - start); // ≈50 (fractional)

Functions

function setTimeout(fn: (...args: any[]) => void, ms: number = 0, ...args: any[]): number

Schedule fn(...args) to run after at least ms milliseconds.

Negative, NaN, and falsy delays are normalized to 0; a 0 delay fires on the next loop turn, after the current synchronous code and any pending microtasks. Extra arguments are forwarded to fn when it fires. The returned numeric id can be passed to clearTimeout() to cancel the timer before it fires. Pending timers keep the process alive.

const id = setTimeout((name) => console.log(name), 10, 'timer');
clearTimeout(id);

function clearTimeout(id: number): void

Cancel a pending setTimeout. No-op if id is unknown or already fired.

Cancellation removes the timer from the event loop immediately, so it no longer keeps the process alive. Clearing the same id more than once is safe, and non-numeric values like undefined or null are ignored.

const id = setTimeout(() => console.log('late'), 1000);
clearTimeout(id);

function setInterval(fn: (...args: any[]) => void, ms: number = 0, ...args: any[]): number

Repeatedly call fn(...args) every ms milliseconds until cancelled.

The next timeout is scheduled only after the callback returns, so slow callbacks stretch the effective period rather than piling up. Delay normalization matches setTimeout: negative, NaN, and falsy delays become 0, which schedules each tick as soon as the loop can run it. The returned id can be passed to clearInterval(); an interval keeps the process alive until it is cleared.

let ticks = 0;
const id = setInterval(() => {
  if (++ticks === 3) clearInterval(id);
}, 1000);

function clearInterval(id: number): void

Cancel a repeating setInterval. No-op if id is unknown.

Shares the same timer state as clearTimeout(), so ids from either API can be cleared through either function without throwing. Cancellation is safe from inside the interval callback itself.

const id = setInterval(() => console.log('tick'), 1000);
clearInterval(id);

function _setTimerRef(id: number, referenced: boolean): void

Change whether a numeric web timer keeps its realm alive. @internal

function _timerHasRef(id: number): boolean

Return whether a numeric web timer keeps its realm alive. @internal

function setImmediate(fn: (...args: any[]) => void, ...args: any[]): number

Schedule fn(...args) to run after the current synchronous turn completes.

Equivalent to setTimeout(fn, 0, ...args): immediate callbacks use the same runtime loop and numeric id space as timers, and run after pending microtasks. Extra arguments are forwarded to fn. The returned id can be passed to clearImmediate() before the callback runs.

const id = setImmediate((name) => console.log(name), 'immediate');
clearImmediate(id);

function clearImmediate(id: number): void

Cancel a pending setImmediate. No-op if id is unknown or already fired.

Alias for clearTimeout() — immediates share the timer id space.

const id = setImmediate(() => console.log('later'));
clearImmediate(id);

function queueMicrotask(fn: () => void): void

Enqueue fn as a microtask — runs before any I/O or timer callbacks but after the current synchronous code completes.

Equivalent to Promise.resolve().then(fn). Microtasks run in registration order, and the callback receives no arguments. Throws a TypeError if fn is not a function.

setTimeout(() => console.log('timer'), 0);
queueMicrotask(() => console.log('microtask')); // logs first