Timer
ExecutionTimer measures elapsed time without creating a trace. The tracing APIs use the same timer internally.
Timings come from performance.now(), so they are monotonic and unaffected by system clock changes.
Usage
Basic
import { ExecutionTimer } from 'execution-engine';
const timer = new ExecutionTimer();
timer.start();
for (let i = 0; i < 1_000_000; i++) {
// work to measure
}
timer.stop();
console.log(timer.getDuration()); // 4.213829994201660
console.log(timer.getElapsedTime()); // "4.213 ms"Multiple timers
One instance can hold many independent timers, each identified by a string. Anything without an explicit id uses 'default':
const timer = new ExecutionTimer();
timer.start('fetch');
await fetchUsers();
timer.stop('fetch');
timer.start('render');
renderUsers();
timer.stop('render');
console.log(timer.getElapsedTime('fetch')); // "128.442 ms"
console.log(timer.getElapsedTime('render')); // "3.118 ms"Timer details
getInfo() returns all the details for one timer as a single object:
console.log(timer.getInfo('fetch', 2, 2));
// {
// executionId: 'fetch',
// startTime: 2025-08-08T15:14:50.118Z,
// endTime: 2025-08-08T15:14:50.246Z,
// duration: 128.44,
// elapsedTime: '128.44 ms'
// }Human-readable output
getElapsedTime() breaks longer durations into readable units:
| Duration | getElapsedTime() |
|---|---|
4.21382999420166 | "4.21382999420166 ms" |
999 | "999 ms" |
1500 | "1 second and 500 ms" |
65000.25 | "1 minute 5 seconds and 0.25 ms" |
Pass fractionDigits to control the millisecond precision: timer.getElapsedTime('default', 3) → "4.214 ms".
Display text only
For whole-second durations, the current formatter can leave a trailing "and", such as "1 minute 5 seconds and". Use getDuration() for calculations and assertions.
API
constructor(executionId?)
Creates a timer identified by executionId, defaulting to 'default'. Construction does not start it.
start(executionId?)
Starts, or restarts, the named timer. Restarting resets both timestamps.
stop(executionId?)
Stops the named timer. Does nothing if that timer was never started.
getDuration(executionId?, fractionDigits?)
Returns the elapsed milliseconds as a number, or undefined if the timer was never started.
fractionDigits— decimal places from0to100. Omit it for full precision.
Stopping is implicit
Calling getDuration() on a running timer stops it before returning the duration. getElapsedTime() does the same because it reads the duration internally.
getElapsedTime(executionId?, fractionDigits?)
Returns the duration as a human-readable string, or undefined if the timer was never started.
getStartDate(executionId?)
Returns the wall-clock Date at which the timer started, or undefined. Derived from performance.timeOrigin plus the recorded offset.
getEndDate(executionId?)
Returns the wall-clock Date at which the timer stopped, or undefined if it has not been stopped.
getInfo(executionId?, durationFractionDigits?, elapsedTimeFractionDigits?)
Returns a TimerDetailsModel for one timer:
interface TimerDetailsModel {
executionId: string;
startTime: Date | undefined;
endTime: Date | undefined;
duration: number | undefined;
elapsedTime: string | undefined;
}The two precision arguments round duration and the millisecond portion of elapsedTime independently.
Stop a running timer before calling getInfo() when you need endTime in that result. getInfo() reads endTime before getDuration() performs its implicit stop.
Remarks
- The individual value getters return
undefinedwhen the requested timer was never started.getInfo()still returns an object, with its unavailable fields set toundefined. getEndDate()returnsundefinedwhile a timer is running. Afterstop()orgetDuration(), it returns the recorded end date.