or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

array-operations.mdfunction-utilities.mdindex.mdmath-operations.mdobject-operations.mdpromise-utilities.mdstring-operations.mdtime-performance.mdtype-guards.md
tile.json

function-utilities.mddocs/

Function Utilities

Function manipulation and execution utilities providing batching, control flow helpers, throttling, debouncing, and functional programming patterns.

Capabilities

Function Execution Control

Utilities for controlling function execution patterns and batching operations.

/**
 * Call every function in an array
 * @param functions - Array of functions to call (null/undefined values are skipped)
 */
function batchInvoke(functions: Nullable<Fn>[]): void;

/**
 * Call the function, returning the result
 * @param fn - Function to invoke
 * @returns Result of the function call
 */
function invoke<T>(fn: () => T): T;

Usage Examples:

import { batchInvoke, invoke } from "@antfu/utils";

// Batch function execution
const cleanupFunctions = [
  () => clearInterval(timer1),
  () => clearTimeout(timeout1),
  null, // Safe to include null values
  () => removeEventListener('click', handler),
  undefined, // Safe to include undefined values
  () => database.disconnect()
];

// Execute all cleanup functions
batchInvoke(cleanupFunctions);

// Event handler management
class EventManager {
  private handlers: Array<(() => void) | null> = [];
  
  addHandler(handler: () => void): void {
    this.handlers.push(handler);
  }
  
  removeHandler(handler: () => void): void {
    const index = this.handlers.indexOf(handler);
    if (index !== -1) {
      this.handlers[index] = null; // Mark as removed
    }
  }
  
  triggerAll(): void {
    batchInvoke(this.handlers);
    this.handlers = this.handlers.filter(h => h !== null); // Cleanup
  }
}

// Lazy evaluation with invoke
const expensiveCalculation = () => {
  console.log('Performing expensive calculation...');
  return Math.random() * 1000;
};

// Only calculate when needed
function processData(useExpensive: boolean) {
  const baseValue = 42;
  const expensiveValue = useExpensive ? invoke(expensiveCalculation) : 0;
  return baseValue + expensiveValue;
}

// Plugin system
const plugins = [
  () => initializePlugin1(),
  () => initializePlugin2(),
  null, // Disabled plugin
  () => initializePlugin3()
];

// Initialize all active plugins
batchInvoke(plugins);

Functional Programming Patterns

Function composition and data transformation utilities.

/**
 * Pass the value through the callback, and return the value
 * @param value - Value to pass through
 * @param callback - Function to call with the value
 * @returns The original value (unchanged)
 */
function tap<T>(value: T, callback: (value: T) => void): T;

Usage Examples:

import { tap } from "@antfu/utils";

// Debugging in function chains
const result = [1, 2, 3, 4, 5]
  .filter(n => n % 2 === 0)
  .map(n => n * 2)
  .tap(arr => console.log('After doubling:', arr)) // Debug without breaking chain
  .reduce((sum, n) => sum + n, 0);

// Object initialization
function createUser(name: string): User {
  return tap(new User(), user => {
    user.name = name;
    user.createdAt = new Date();
    user.id = generateId();
  });
}

// Configuration with side effects
const config = tap(loadConfig(), config => {
  validateConfig(config);
  logConfigLoad(config);
  sendTelemetry('config-loaded', config.version);
});

// DOM manipulation
const element = tap(document.createElement('div'), div => {
  div.className = 'my-component';
  div.setAttribute('data-id', '123');
  div.addEventListener('click', handleClick);
});

// API response processing
async function fetchUserData(userId: string) {
  return fetch(`/api/users/${userId}`)
    .then(response => response.json())
    .then(user => tap(user, user => {
      // Log user access without affecting the data flow
      analytics.track('user-data-accessed', { userId: user.id });
      cache.set(`user:${userId}`, user);
    }));
}

// Conditional side effects
function processOrder(order: Order): Order {
  return tap(order, order => {
    if (order.total > 1000) {
      notifyManager('large-order', order);
    }
    
    if (order.isUrgent) {
      priorityQueue.add(order);
    }
  });
}

// Fluent API building
class QueryBuilder {
  private query = '';
  
  select(fields: string): this {
    return tap(this, () => {
      this.query += `SELECT ${fields} `;
    });
  }
  
  from(table: string): this {
    return tap(this, () => {
      this.query += `FROM ${table} `;
    });
  }
  
  where(condition: string): this {
    return tap(this, () => {
      this.query += `WHERE ${condition} `;
    });
  }
  
  build(): string {
    return this.query.trim();
  }
}

// Usage: const sql = new QueryBuilder().select('*').from('users').where('active = 1').build();

Throttling and Debouncing

Advanced function rate limiting with cancellation support.

interface CancelOptions {
  upcomingOnly?: boolean;
}

interface ReturnWithCancel<T extends (...args: any[]) => any> {
  (...args: Parameters<T>): void;
  cancel: (options?: CancelOptions) => void;
}

/**
 * Throttle function execution - limits calls to at most once per specified time period
 * @param delay - Delay in milliseconds
 * @param callback - Function to throttle
 * @param options - Optional configuration
 * @returns Throttled function with cancel method
 */
function throttle<T extends (...args: any[]) => any>(
  delay: number, 
  callback: T, 
  options?: { noTrailing?: boolean; noLeading?: boolean; debounceMode?: boolean }
): ReturnWithCancel<T>;

/**
 * Debounce function execution - delays execution until after specified time has passed since last call
 * @param delay - Delay in milliseconds
 * @param callback - Function to debounce
 * @param options - Optional configuration
 * @returns Debounced function with cancel method
 */
function debounce<T extends (...args: any[]) => any>(
  delay: number, 
  callback: T, 
  options?: { atBegin?: boolean }
): ReturnWithCancel<T>;

Usage Examples:

import { throttle, debounce } from "@antfu/utils";

// Throttled scroll handler (executes at most once every 100ms)
const handleScroll = throttle(100, () => {
  console.log('Scroll position:', window.scrollY);
  updateScrollIndicator();
});

window.addEventListener('scroll', handleScroll);

// Debounced search (waits 300ms after user stops typing)
const performSearch = debounce(300, (query: string) => {
  console.log('Searching for:', query);
  searchAPI(query);
});

const searchInput = document.getElementById('search') as HTMLInputElement;
searchInput.addEventListener('input', (e) => {
  performSearch((e.target as HTMLInputElement).value);
});

// Throttled API calls for real-time data
const updateMetrics = throttle(5000, async () => {
  const metrics = await fetchLatestMetrics();
  updateDashboard(metrics);
});

// Auto-save with debouncing
const autoSave = debounce(2000, (data: any) => {
  localStorage.setItem('draft', JSON.stringify(data));
  console.log('Draft saved automatically');
});

// Window resize handling
const handleResize = throttle(150, () => {
  updateLayout();
  recalculatePositions();
});

window.addEventListener('resize', handleResize);

// Button click protection (prevent double-clicks)
const submitForm = debounce(1000, async (formData: FormData) => {
  try {
    await api.submitForm(formData);
    showSuccessMessage();
  } catch (error) {
    showErrorMessage(error);
  }
});

// Cancellation examples
const debouncedSave = debounce(1000, saveData);

// Cancel pending saves before navigation
function beforeNavigate() {
  debouncedSave.cancel(); // Cancel any pending execution
}

// Cancel only upcoming calls, let current one complete
function softCancel() {
  debouncedSave.cancel({ upcomingOnly: true });
}

// Mouse tracking with throttling
const trackMouse = throttle(50, (x: number, y: number) => {
  updateCursor(x, y);
  sendMouseData({ x, y, timestamp: Date.now() });
});

document.addEventListener('mousemove', (e) => {
  trackMouse(e.clientX, e.clientY);
});

// Real-world example: Search suggestions
class SearchWidget {
  private suggestionsDebounced = debounce(300, this.loadSuggestions.bind(this));
  private metricsThrottled = throttle(1000, this.trackSearchMetrics.bind(this));
  
  onInput(query: string): void {
    this.suggestionsDebounced(query);
    this.metricsThrottled(query);
  }
  
  private async loadSuggestions(query: string): Promise<void> {
    if (query.length < 2) return;
    
    const suggestions = await this.api.getSuggestions(query);
    this.displaySuggestions(suggestions);
  }
  
  private trackSearchMetrics(query: string): void {
    this.analytics.track('search-input', { query, length: query.length });
  }
  
  destroy(): void {
    // Clean up any pending calls
    this.suggestionsDebounced.cancel();
    this.metricsThrottled.cancel();
  }
}