Skip to main content

Overview

TimeUtils is a class providing comprehensive timing utilities for async operations, including debouncing, throttling, delays, timeouts, retries with exponential backoff, promise management, and performance measurement.

Import

import { TimeUtils } from '@bytekit/utils/helpers';

Methods

debounce

Creates a debounced function that delays execution until after wait ms have elapsed since the last call.
fn
T extends (...args: unknown[]) => unknown
required
The function to debounce
wait
number
required
Milliseconds to wait before execution
options
DebounceOptions
Optional configuration
leading
boolean
default:"false"
Execute on the leading edge
trailing
boolean
default:"true"
Execute on the trailing edge
maxWait
number
Maximum time to wait before forcing execution
Returns: (...args: Parameters<T>) => void - Debounced function
// Basic debounce - waits for user to stop typing
const handleSearch = TimeUtils.debounce((query: string) => {
  console.log('Searching for:', query);
}, 300);

handleSearch('h');
handleSearch('he');
handleSearch('hel');
handleSearch('hell');
handleSearch('hello');
// Only logs once: "Searching for: hello" (after 300ms of no calls)

// With leading edge
const handleClick = TimeUtils.debounce(
  () => console.log('Clicked'),
  1000,
  { leading: true, trailing: false }
);

// Executes immediately on first call, ignores subsequent calls
handleClick(); // Logs immediately
handleClick(); // Ignored
handleClick(); // Ignored

// With maxWait to prevent indefinite delays
const handleScroll = TimeUtils.debounce(
  () => console.log('Scroll ended'),
  500,
  { maxWait: 2000 }
);
// Guaranteed to execute at least every 2 seconds during continuous scrolling
Use Cases:
  • Search input handlers
  • Window resize handlers
  • Auto-save functionality
  • API request throttling

throttle

Creates a throttled function that executes at most once every wait ms.
fn
T extends (...args: unknown[]) => unknown
required
The function to throttle
wait
number
required
Milliseconds between allowed executions
options
ThrottleOptions
Optional configuration
leading
boolean
default:"true"
Execute on the leading edge
trailing
boolean
default:"true"
Execute on the trailing edge
Returns: (...args: Parameters<T>) => void - Throttled function
// Basic throttle - limits execution rate
const handleMouseMove = TimeUtils.throttle((e: MouseEvent) => {
  console.log('Mouse at:', e.clientX, e.clientY);
}, 100);

// Logs at most every 100ms during mouse movement

// With trailing edge only
const handleInput = TimeUtils.throttle(
  (value: string) => console.log('Input:', value),
  1000,
  { leading: false, trailing: true }
);

// Without leading edge
const trackScroll = TimeUtils.throttle(
  () => console.log('Scrolled'),
  500,
  { leading: false }
);
Use Cases:
  • Mouse move/scroll handlers
  • Button click prevention
  • API polling rate limiting
  • Animation frame updates

sleep

Pauses execution for specified milliseconds.
ms
number
required
Milliseconds to sleep
Returns: Promise<void> - Promise that resolves after delay
async function processSteps() {
  console.log('Step 1');
  await TimeUtils.sleep(1000);
  console.log('Step 2');
  await TimeUtils.sleep(2000);
  console.log('Step 3');
}

// Rate limiting API requests
async function fetchBatch(ids: string[]) {
  const results = [];
  for (const id of ids) {
    results.push(await fetchData(id));
    await TimeUtils.sleep(100); // Wait 100ms between requests
  }
  return results;
}

delay

Delays execution of a function and returns its result.
fn
() => T
required
Function to execute after delay
ms
number
required
Milliseconds to wait before execution
Returns: Promise<T> - Promise resolving to function result
const result = await TimeUtils.delay(() => {
  return 'Hello';
}, 1000);
console.log(result); // Logs 'Hello' after 1 second

// With async function
const data = await TimeUtils.delay(async () => {
  return await fetchData();
}, 500);

// Scheduled notification
await TimeUtils.delay(() => {
  showNotification('Time is up!');
}, 5000);

timeout

Executes a promise with a timeout limit.
promise
Promise<T>
required
Promise to execute with timeout
ms
number
required
Timeout in milliseconds
message
string
default:"Operation timed out"
Error message if timeout occurs
Returns: Promise<T> - Promise that rejects if timeout is reached Throws: Error if timeout is reached
try {
  const data = await TimeUtils.timeout(
    fetch('https://api.example.com/data'),
    5000
  );
  console.log('Success:', data);
} catch (error) {
  console.error('Failed or timed out:', error);
}

// Custom timeout message
try {
  const result = await TimeUtils.timeout(
    slowOperation(),
    3000,
    'Operation took too long'
  );
} catch (error) {
  console.error(error.message); // 'Operation took too long'
}

// Timeout for user input
const response = await TimeUtils.timeout(
  waitForUserInput(),
  10000,
  'User input timeout'
);

retryAsync

Retries an async function with exponential backoff on failure.
fn
() => Promise<T>
required
Async function to retry
options
object
Retry configuration
maxAttempts
number
default:"3"
Maximum number of attempts
initialDelayMs
number
default:"100"
Initial delay in milliseconds
maxDelayMs
number
default:"10000"
Maximum delay in milliseconds
backoffMultiplier
number
default:"2"
Multiplier for exponential backoff
Returns: Promise<T> - Promise resolving to function result Throws: Last error if all attempts fail
// Basic retry
const data = await TimeUtils.retryAsync(async () => {
  return await fetch('https://api.example.com/data');
});

// Custom retry configuration
const result = await TimeUtils.retryAsync(
  async () => {
    const response = await fetch('/api/data');
    if (!response.ok) throw new Error('Request failed');
    return response.json();
  },
  {
    maxAttempts: 5,
    initialDelayMs: 500,
    maxDelayMs: 30000,
    backoffMultiplier: 2
  }
);

// Delays between attempts:
// Attempt 1: immediate
// Attempt 2: 500ms delay
// Attempt 3: 1000ms delay
// Attempt 4: 2000ms delay
// Attempt 5: 4000ms delay
Backoff Calculation:
delay = min(initialDelayMs * (backoffMultiplier ^ (attempt - 1)), maxDelayMs)

race

Races multiple promises, returning the first to resolve.
promises
Promise<T>[]
required
Array of promises to race
Returns: Promise<T> - First promise to resolve
const fastest = await TimeUtils.race([
  fetch('https://api1.example.com/data'),
  fetch('https://api2.example.com/data'),
  fetch('https://api3.example.com/data')
]);
// Returns result from fastest API

// With timeout
const result = await TimeUtils.race([
  fetchData(),
  TimeUtils.sleep(5000).then(() => null)
]);
// Returns data or null if takes longer than 5 seconds

all

Waits for all promises to resolve.
promises
Promise<T>[]
required
Array of promises to wait for
Returns: Promise<T[]> - Array of all resolved values Throws: If any promise rejects
const [users, posts, comments] = await TimeUtils.all([
  fetchUsers(),
  fetchPosts(),
  fetchComments()
]);

console.log('All data loaded:', { users, posts, comments });

allSettled

Waits for all promises to settle (resolve or reject).
promises
Promise<T>[]
required
Array of promises to wait for
Returns: Promise<PromiseSettledResult<T>[]> - Array of settled results
const results = await TimeUtils.allSettled([
  fetchUsers(),
  fetchPosts(),
  fetchComments()
]);

results.forEach((result, index) => {
  if (result.status === 'fulfilled') {
    console.log(`Promise ${index} succeeded:`, result.value);
  } else {
    console.error(`Promise ${index} failed:`, result.reason);
  }
});

// Process only successful results
const successful = results
  .filter(r => r.status === 'fulfilled')
  .map(r => r.value);

createQueue

Creates a queue for managing concurrent async operations.
concurrency
number
default:"1"
Maximum number of concurrent operations
Returns: Object with add and size methods
const queue = TimeUtils.createQueue(3);

// Add tasks to queue
const tasks = ids.map(id => 
  queue.add(() => processItem(id))
);

// Wait for all tasks
const results = await Promise.all(tasks);

// Check queue size
console.log('Queue size:', queue.size());

// Example: Rate-limited API calls
const apiQueue = TimeUtils.createQueue(5);

for (const userId of userIds) {
  apiQueue.add(async () => {
    const data = await fetch(`/api/users/${userId}`);
    return data.json();
  });
}
Queue Methods:
  • add<T>(fn: () => Promise<T>): Promise<T> - Add task to queue
  • size(): number - Current queue size including running tasks

measureAsync

Measures execution time of an async function.
fn
() => Promise<T>
required
Async function to measure
Returns: Promise<{ result: T; durationMs: number }> - Result and duration
const { result, durationMs } = await TimeUtils.measureAsync(async () => {
  const data = await fetchData();
  return processData(data);
});

console.log(`Operation completed in ${durationMs}ms`);
console.log('Result:', result);

// Performance monitoring
const metrics = await TimeUtils.measureAsync(() => heavyComputation());
if (metrics.durationMs > 1000) {
  console.warn('Slow operation detected:', metrics.durationMs);
}

measureSync

Measures execution time of a synchronous function.
fn
() => T
required
Function to measure
Returns: { result: T; durationMs: number } - Result and duration
const { result, durationMs } = TimeUtils.measureSync(() => {
  let sum = 0;
  for (let i = 0; i < 1000000; i++) {
    sum += i;
  }
  return sum;
});

console.log(`Calculation took ${durationMs}ms`);
console.log('Sum:', result);

formatDuration

Formats milliseconds into a human-readable string.
ms
number
required
Duration in milliseconds
Returns: string - Formatted duration
TimeUtils.formatDuration(500);
// Returns: '500ms'

TimeUtils.formatDuration(2500);
// Returns: '2.50s'

TimeUtils.formatDuration(65000);
// Returns: '1.08m'

TimeUtils.formatDuration(3600000);
// Returns: '1.00h'

TimeUtils.formatDuration(7200000);
// Returns: '2.00h'
Format Rules:
  • < 1000ms: Shows as milliseconds
  • < 60s: Shows as seconds (2 decimals)
  • < 60m: Shows as minutes (2 decimals)
  • ≥ 60m: Shows as hours (2 decimals)

parseDuration

Parses a duration string to milliseconds.
duration
string
required
Duration string (e.g., ‘500ms’, ‘2s’, ‘5m’)
Returns: number - Duration in milliseconds Throws: Error if format is invalid
TimeUtils.parseDuration('500ms');
// Returns: 500

TimeUtils.parseDuration('2.5s');
// Returns: 2500

TimeUtils.parseDuration('5m');
// Returns: 300000

TimeUtils.parseDuration('1.5h');
// Returns: 5400000

TimeUtils.parseDuration('2d');
// Returns: 172800000

// Invalid format
TimeUtils.parseDuration('invalid');
// Throws: Error: Invalid duration format: invalid
Supported Units:
  • ms - Milliseconds
  • s - Seconds
  • m - Minutes
  • h - Hours
  • d - Days

Type Definitions

type DebounceOptions = {
  leading?: boolean;
  trailing?: boolean;
  maxWait?: number;
};

type ThrottleOptions = {
  leading?: boolean;
  trailing?: boolean;
};

Best Practices

Debounce vs Throttle

Use debounce when:
  • User input needs to settle (search, form validation)
  • Final value matters (auto-save)
  • Action should only happen after activity stops
Use throttle when:
  • Continuous events need sampling (scroll, mouse move)
  • Regular updates needed (progress tracking)
  • Rate limiting API calls

Error Handling with Retry

try {
  const data = await TimeUtils.retryAsync(
    async () => {
      const response = await fetch('/api/data');
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      return response.json();
    },
    { maxAttempts: 3 }
  );
} catch (error) {
  console.error('Failed after retries:', error);
  // Handle permanent failure
}

Concurrent Operations with Queue

// Limit concurrent downloads
const downloadQueue = TimeUtils.createQueue(3);

const downloads = fileUrls.map(url =>
  downloadQueue.add(async () => {
    console.log('Downloading:', url);
    const response = await fetch(url);
    return response.blob();
  })
);

const files = await Promise.all(downloads);

Performance Monitoring

const monitorPerformance = async (name: string, fn: () => Promise<any>) => {
  const { result, durationMs } = await TimeUtils.measureAsync(fn);
  
  console.log(`[${name}] completed in ${TimeUtils.formatDuration(durationMs)}`);
  
  if (durationMs > 5000) {
    console.warn(`[${name}] exceeded threshold`);
  }
  
  return result;
};

await monitorPerformance('fetchUsers', () => fetchUsers());