Skip to content

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

ts
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':

ts
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:

ts
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:

DurationgetElapsedTime()
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 from 0 to 100. 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:

ts
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 undefined when the requested timer was never started. getInfo() still returns an object, with its unavailable fields set to undefined.
  • getEndDate() returns undefined while a timer is running. After stop() or getDuration(), it returns the recorded end date.

See also

  • trace — automatic timing as part of a full execution trace.
  • Trace — where duration and elapsedTime land on a trace node.

Released under the MIT License.