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

promise-utilities.mddocs/

Promise & Async Utilities

Advanced promise management utilities providing singleton promises, promise locks, multi-promise handling, and async control flow patterns.

Capabilities

Singleton Promises

Create promise functions that ensure only one instance runs at a time.

interface SingletonPromiseReturn<T> {
  (): Promise<T>;
  /**
   * Reset current staled promise.
   * Await it to have proper shutdown.
   */
  reset: () => Promise<void>;
}

/**
 * Create singleton promise function
 * @param fn - Function that returns a promise
 * @returns Singleton promise function with reset capability
 */
function createSingletonPromise<T>(fn: () => Promise<T>): SingletonPromiseReturn<T>;

Usage Examples:

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

// API call that should only happen once at a time
const fetchUser = createSingletonPromise(async () => {
  const response = await fetch('/api/user');
  return response.json();
});

// Multiple calls will share the same promise
const user1Promise = fetchUser(); // Starts API call
const user2Promise = fetchUser(); // Reuses same promise
const user3Promise = fetchUser(); // Reuses same promise

console.log(user1Promise === user2Promise); // true

// Reset to allow new calls
await fetchUser.reset();
const newUserPromise = fetchUser(); // Fresh API call

// Heavy computation singleton
const expensiveComputation = createSingletonPromise(async () => {
  console.log('Starting expensive computation...');
  await new Promise(resolve => setTimeout(resolve, 5000));
  return 'computed result';
});

// Database connection singleton
const connectToDatabase = createSingletonPromise(async () => {
  console.log('Connecting to database...');
  const connection = await database.connect();
  return connection;
});

Sleep & Timing

Async timing utilities for delays and scheduling.

/**
 * Promised `setTimeout`
 * @param ms - Milliseconds to wait
 * @param callback - Optional callback to execute after delay
 * @returns Promise that resolves after the specified time
 */
function sleep(ms: number, callback?: Fn<any>): Promise<void>;

Usage Examples:

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

// Simple delay
async function example() {
  console.log('Starting...');
  await sleep(1000); // Wait 1 second
  console.log('Done!');
}

// Delay with callback
await sleep(2000, () => {
  console.log('This runs after 2 seconds');
});

// Retry logic with exponential backoff
async function retryWithBackoff<T>(
  fn: () => Promise<T>, 
  maxRetries: number = 3
): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      
      const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s, 8s...
      console.log(`Retrying in ${delay}ms...`);
      await sleep(delay);
    }
  }
  throw new Error('Max retries exceeded');
}

// Animation timing
async function fadeIn(element: HTMLElement) {
  element.style.opacity = '0';
  element.style.display = 'block';
  
  for (let opacity = 0; opacity <= 1; opacity += 0.1) {
    element.style.opacity = opacity.toString();
    await sleep(50); // 50ms between steps
  }
}

// Rate limiting
async function rateLimitedRequests(urls: string[]) {
  const results = [];
  for (const url of urls) {
    const response = await fetch(url);
    results.push(await response.json());
    await sleep(100); // 100ms between requests
  }
  return results;
}

Promise Locks

Coordinate async operations with promise-based locking mechanisms.

interface PromiseLock {
  /**
   * Run a function with the lock
   * @param fn - Async function to run
   * @returns Promise resolving to the function's result
   */
  run<T = void>(fn: () => Promise<T>): Promise<T>;
  
  /**
   * Wait for all running tasks to complete
   * @returns Promise that resolves when all tasks are done
   */
  wait(): Promise<void>;
  
  /**
   * Check if any tasks are currently running
   * @returns True if tasks are running
   */
  isWaiting(): boolean;
  
  /**
   * Clear all locks (does not wait for completion)
   */
  clear(): void;
}

/**
 * Create a promise lock for coordinating async operations
 * @returns Promise lock instance
 */
function createPromiseLock(): PromiseLock;

Usage Examples:

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

// Resource access coordination
const dbLock = createPromiseLock();

async function updateUser(userId: string, data: any) {
  return dbLock.run(async () => {
    const user = await db.findUser(userId);
    const updated = { ...user, ...data };
    return db.saveUser(updated);
  });
}

// File system operations
const fileLock = createPromiseLock();

async function writeToFile(filename: string, content: string) {
  return fileLock.run(async () => {
    await fs.writeFile(filename, content);
    console.log(`Written to ${filename}`);
  });
}

// Background task coordination
const backgroundTasks = createPromiseLock();

// Start some background work
backgroundTasks.run(async () => {
  await processImages();
});

backgroundTasks.run(async () => {
  await generateReports();
});

// Wait for all background tasks to complete before shutdown
async function gracefulShutdown() {
  console.log('Waiting for background tasks...');
  await backgroundTasks.wait();
  console.log('All tasks completed, shutting down');
}

// Check if work is in progress
if (backgroundTasks.isWaiting()) {
  console.log('Background tasks are still running');
}

Controlled Promises

Create promises with externally accessible resolve and reject methods.

interface ControlledPromise<T = void> extends Promise<T> {
  resolve: (value: T | PromiseLike<T>) => void;
  reject: (reason?: any) => void;
}

/**
 * Return a Promise with `resolve` and `reject` methods
 * @returns Promise with exposed resolve/reject methods
 */
function createControlledPromise<T>(): ControlledPromise<T>;

Usage Examples:

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

// Manual promise control
const manualPromise = createControlledPromise<string>();

// Resolve from another context
setTimeout(() => {
  manualPromise.resolve('Hello from timeout!');
}, 2000);

const result = await manualPromise; // Waits for manual resolution

// Event-driven promises
function waitForEvent(element: EventTarget, eventName: string) {
  const promise = createControlledPromise<Event>();
  
  const handler = (event: Event) => {
    element.removeEventListener(eventName, handler);
    promise.resolve(event);
  };
  
  element.addEventListener(eventName, handler);
  return promise;
}

// Usage: await waitForEvent(button, 'click');

// Queue management
class TaskQueue {
  private pending = new Map<string, ControlledPromise<any>>();
  
  async waitForTask(taskId: string) {
    if (!this.pending.has(taskId)) {
      this.pending.set(taskId, createControlledPromise());
    }
    return this.pending.get(taskId)!;
  }
  
  completeTask(taskId: string, result: any) {
    const promise = this.pending.get(taskId);
    if (promise) {
      promise.resolve(result);
      this.pending.delete(taskId);
    }
  }
  
  failTask(taskId: string, error: any) {
    const promise = this.pending.get(taskId);
    if (promise) {
      promise.reject(error);
      this.pending.delete(taskId);
    }
  }
}

// Communication between components
const communicationBridge = createControlledPromise<{ type: string; data: any }>();

// Component A waits for message
const message = await communicationBridge;
console.log('Received:', message);

// Component B sends message
communicationBridge.resolve({ type: 'user-action', data: { userId: 123 } });

Multi-Promise Utilities

Advanced utilities for handling multiple promises with transformation and filtering capabilities.

interface POptions {
  /**
   * How many promises are resolved at the same time.
   */
  concurrency?: number | undefined;
}

interface PInstance<T = any> extends Promise<Awaited<T>[]> {
  /** Add more promises to the collection */
  add(...args: (T | Promise<T>)[]): void;
  
  /** Transform resolved values */
  map<U>(fn: (value: Awaited<T>, index: number) => U): PInstance<Promise<U>>;
  
  /** Filter resolved values */
  filter(fn: (value: Awaited<T>, index: number) => boolean | Promise<boolean>): PInstance<Promise<T>>;
  
  /** Execute function for each resolved value */
  forEach(fn: (value: Awaited<T>, index: number) => void): Promise<void>;
  
  /** Reduce resolved values */
  reduce<U>(fn: (previousValue: U, currentValue: Awaited<T>, currentIndex: number, array: Awaited<T>[]) => U, initialValue: U): Promise<U>;
  
  /** Clear all promises */
  clear(): void;
}

/**
 * Utility for managing multiple promises
 * @param items - Initial items to process
 * @param options - Configuration options
 * @returns PInstance for chaining operations
 */
function p<T = any>(items?: Iterable<T>, options?: POptions): PInstance<T>;

Usage Examples:

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

// Basic promise chaining
const items = [1, 2, 3, 4, 5];

const results = await p(items)
  .map(async i => {
    await sleep(100); // Simulate async work
    return i * 3;
  })
  .filter(async i => {
    await sleep(50); // Simulate async filtering
    return i % 2 === 0;
  });
// Results: [6, 12]

// Concurrency control
const urls = ['url1', 'url2', 'url3', 'url4', 'url5'];

const responses = await p(urls, { concurrency: 2 })
  .map(async url => {
    const response = await fetch(url);
    return response.json();
  });

// Dynamic promise management
const promiseManager = p<string>();

// Add promises dynamically
promiseManager.add(
  fetch('/api/users').then(r => r.text()),
  fetch('/api/posts').then(r => r.text()),
  Promise.resolve('static-data')
);

// Process when ready
const data = await promiseManager.map(text => JSON.parse(text));

// Batch processing with error handling
async function processBatch(items: any[]) {
  return p(items)
    .map(async item => {
      try {
        return await processItem(item);
      } catch (error) {
        console.error(`Failed to process item:`, item, error);
        return null;
      }
    })
    .filter(result => result !== null);
}

// Data pipeline
const pipeline = p(['data1', 'data2', 'data3'])
  .map(async data => await validate(data))
  .filter(async data => await isValid(data))
  .map(async data => await transform(data))
  .reduce(async (acc, item) => {
    acc.push(await finalize(item));
    return acc;
  }, []);

const finalResult = await pipeline;