ts-time-utils
Version:
A comprehensive TypeScript utility library for time, dates, durations, and calendar operations with full tree-shaking support
222 lines (221 loc) • 6.14 kB
JavaScript
/**
* Async time utilities for delays, timeouts, and performance
*/
/**
* Sleep for a specified number of milliseconds
* @param ms - milliseconds to sleep
*/
export function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Add a timeout to any promise
* @param promise - promise to add timeout to
* @param ms - timeout in milliseconds
* @param timeoutMessage - optional timeout error message
*/
export function timeout(promise, ms, timeoutMessage = 'Operation timed out') {
return Promise.race([
promise,
new Promise((_, reject) => setTimeout(() => reject(new Error(timeoutMessage)), ms))
]);
}
/**
* Debounce function - delays execution until after delay has passed since last call
* @param fn - function to debounce
* @param delay - delay in milliseconds
*/
export function debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
/**
* Throttle function - limits execution to once per delay period
* @param fn - function to throttle
* @param delay - delay in milliseconds
*/
export function throttle(fn, delay) {
let lastCall = 0;
return (...args) => {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
fn(...args);
}
};
}
/**
* Retry a promise-returning function with exponential backoff
* @param fn - function that returns a promise
* @param maxAttempts - maximum number of attempts
* @param baseDelay - base delay in milliseconds
* @param maxDelay - maximum delay in milliseconds
*/
export async function retry(fn, maxAttempts = 3, baseDelay = 1000, maxDelay = 10000) {
let lastError;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
}
catch (error) {
lastError = error;
if (attempt === maxAttempts) {
throw lastError;
}
// Exponential backoff with jitter
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1) + Math.random() * 1000, maxDelay);
await sleep(delay);
}
}
throw lastError;
}
/**
* Stopwatch for measuring elapsed time
*/
export class Stopwatch {
constructor() {
this.startTime = null;
this.endTime = null;
this.pausedTime = 0;
this.pauseStart = null;
}
/**
* Start the stopwatch
*/
start() {
if (this.startTime !== null) {
throw new Error('Stopwatch is already running');
}
this.startTime = performance.now();
this.endTime = null;
this.pausedTime = 0;
this.pauseStart = null;
}
/**
* Stop the stopwatch
*/
stop() {
if (this.startTime === null) {
throw new Error('Stopwatch is not running');
}
if (this.pauseStart !== null) {
this.pausedTime += performance.now() - this.pauseStart;
this.pauseStart = null;
}
this.endTime = performance.now();
const elapsed = this.endTime - this.startTime - this.pausedTime;
return elapsed;
}
/**
* Pause the stopwatch
*/
pause() {
if (this.startTime === null) {
throw new Error('Stopwatch is not running');
}
if (this.pauseStart !== null) {
throw new Error('Stopwatch is already paused');
}
this.pauseStart = performance.now();
}
/**
* Resume the stopwatch
*/
resume() {
if (this.pauseStart === null) {
throw new Error('Stopwatch is not paused');
}
this.pausedTime += performance.now() - this.pauseStart;
this.pauseStart = null;
}
/**
* Reset the stopwatch
*/
reset() {
this.startTime = null;
this.endTime = null;
this.pausedTime = 0;
this.pauseStart = null;
}
/**
* Get elapsed time without stopping
*/
getElapsed() {
if (this.startTime === null) {
return 0;
}
const now = performance.now();
let pausedTime = this.pausedTime;
if (this.pauseStart !== null) {
pausedTime += now - this.pauseStart;
}
return now - this.startTime - pausedTime;
}
/**
* Check if stopwatch is running
*/
isRunning() {
return this.startTime !== null && this.endTime === null && this.pauseStart === null;
}
/**
* Check if stopwatch is paused
*/
isPaused() {
return this.pauseStart !== null;
}
}
/**
* Create a new stopwatch instance
*/
export function createStopwatch() {
return new Stopwatch();
}
/**
* Measure execution time of a synchronous function
* @param fn - function to measure
* @returns tuple of [result, elapsed time in ms]
*/
export function measureTime(fn) {
const start = performance.now();
const result = fn();
const elapsed = performance.now() - start;
return [result, elapsed];
}
/**
* Measure execution time of an asynchronous function
* @param fn - async function to measure
* @returns promise that resolves to tuple of [result, elapsed time in ms]
*/
export async function measureAsync(fn) {
const start = performance.now();
const result = await fn();
const elapsed = performance.now() - start;
return [result, elapsed];
}
/**
* Benchmark a function by running it multiple times
* @param fn - function to benchmark
* @param iterations - number of iterations to run
*/
export function benchmark(fn, iterations = 1000) {
const times = [];
for (let i = 0; i < iterations; i++) {
const [, elapsed] = measureTime(fn);
times.push(elapsed);
}
const totalTime = times.reduce((sum, time) => sum + time, 0);
const average = totalTime / iterations;
const min = Math.min(...times);
const max = Math.max(...times);
return {
totalTime,
average,
min,
max,
iterations,
total: totalTime
};
}