UNPKG

@oazmi/kitchensink

Version:

a collection of personal utility functions

161 lines (160 loc) 5.93 kB
/** this submodule has some basic tools for timing functions. * * @module */ import { date_now, performance_now } from "./alias.js"; import { DEBUG } from "./deps.js"; const parseTimeFn = (time_fn) => { return time_fn === "perf" ? performance_now : time_fn === "date" ? date_now : time_fn; }; /** time the execution of a function. * * @returns millisecond time for function execution. * * @example * ```ts * import { * assertLessOrEqual as assertLe, * assertGreaterOrEqual as assertGe, * } from "jsr:@std/assert" * * const * testFn = (a: number, b: number): number => { return a + b }, * delta_time = timeIt(testFn, 5, 10), * log = `execution time: ${delta_time} ms` * * assertGe(delta_time, 0) // computing a sum should take more than `0` milliseconds * assertLe(delta_time, 2) // computing a sum should take less than `2` milliseconds * ``` */ export const timeIt = (fn, ...args) => { const t1 = performance_now(); fn(...args); return performance_now() - t1; }; /** asynchronously time the execution of an async function. * * if you are going to provide a synchronous function with certainty, then you might be better off using {@link timeIt} instead. * * @returns millisecond time for function execution. * * @example * ```ts * import { * assertLessOrEqual as assertLe, * assertGreaterOrEqual as assertGe, * } from "jsr:@std/assert" * * const asyncTestFn = async (a: number, b: number): Promise<number> => new Promise((resolve) => { * setTimeout(() => { resolve(a + b) }, 300) * }) * const * delta_time = await asyncTimeIt(asyncTestFn, 5, 10), * log = `execution time: ${delta_time} ms` * * assertGe(delta_time, 290) // completing the promise should take more than `290` milliseconds * assertLe(delta_time, 400) // completing the promise should take less than `400` milliseconds * ``` */ export const asyncTimeIt = async (fn, ...args) => { const t1 = performance_now(); await fn(...args); return performance_now() - t1; }; /** a stopwatch class that provides convince methods for timing. * * this module exports a global {@link defaultStopwatch} available to all importing scripts, * which is beneficial if you want to use a single stop watch across many modules, * otherwise, if you want a dedicated stopwatch, you can create a new instance of this class. * * this stopwatch operates on the principals of a stack data-structure: * - that is, you can `push`, `pop`, and `seek` the time. * - the `Delta` methods provide the *elapsed* time since the last `push`. * * @example * ```ts * import { * assertLessOrEqual as assertLe, * assertGreaterOrEqual as assertGe, * assertThrows, * } from "jsr:@std/assert" * * const stop_watch = new Stopwatch("perf") * stop_watch.push() * * const * resolved_value: "hello" = await (new Promise((resolve) => { * setTimeout(() => { resolve("hello") }, 300) * })), * delta_time = stop_watch.popDelta(), * log = `execution time: ${delta_time} ms` * * assertGe(delta_time, 290) // completing the promise should take more than `290` milliseconds * assertLe(delta_time, 400) // completing the promise should take less than `400` milliseconds * * // the stack of `stop_watch` is empty now, so trying to `popDelta` will throw an error (intentional by design) * assertThrows(() => stop_watch.popDelta()) * ``` */ export class Stopwatch { /** the stack in which we push timestamps. */ stack = []; /** a function that returns the current time */ time; constructor(get_time_fn = "perf") { this.time = parseTimeFn(get_time_fn); } /** get the current time. */ getTime() { return this.time(); } /** push the current time into the stack, and get the value of the current time returned. */ push() { const current_time = this.time(); this.stack.push(current_time); return current_time; } /** push the current time into the stack, and get the time elapsed since the last push. * if this is the first push, then the returned value will be `undefined`. */ pushDelta() { const current_time = this.time(), prev_time = this.seek(); this.stack.push(current_time); return prev_time === undefined ? prev_time : current_time - prev_time; } /** pop the top most time from the time stack. * if the time stack is empty, then an `undefined` will be returned. */ pop() { return this.stack.pop(); } /** get the time elapsed since the most recent push into the time stack, and also pop. * * @throws `Error` this function will throw an error if the time stack was already empty. * this is intentional, since it would hint that you are using this method non-deterministically, and thus incorrectly. */ popDelta() { const current_time = this.time(), prev_time = this.pop(); if (prev_time === undefined) { throw new Error(DEBUG.ERROR ? "there was nothing in the time stack to pop" : ""); } return current_time - prev_time; } /** preview the top most time in the stack without popping. * if the time stack is empty, then an `undefined` will be returned. */ seek() { return this.stack.at(-1); } /** preview the time elapsed since the most recent push into the time stack, without popping. * if there is nothing in the time stack, then an `undefined` will be returned. */ seekDelta() { const current_time = this.time(), prev_time = this.seek(); return prev_time === undefined ? prev_time : current_time - prev_time; } } /** a default instance of {@link Stopwatch} (in `performance.now` timer mode) is exported for convenience, * so that it is easily shareable among all libraries that import this submodule. */ export const defaultStopwatch = /*@__PURE__*/ new Stopwatch("perf");