or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

array-operations.mdasync-promise-utilities.mddata-utilities.mdfunction-utilities.mdindex.mdobject-operations.mdpath-operations.mdperformance-benchmarking.mdstring-utilities.mdvalidation-assertions.md
tile.json

async-promise-utilities.mddocs/

Async and Promise Utilities

Promise-based utilities for timing control, blocking, and promise detection in async workflows with support for various timeout scenarios and promise identification.

Capabilities

Wait

Returns a Promise that resolves after the requested timeout with optional return value and custom setTimeout support.

/**
 * Returns a Promise that resolves after the requested timeout
 * @param timeout - The number of milliseconds to wait before resolving the Promise
 * @param returnValue - The value that the Promise will resolve to
 * @param options - Optional settings with custom setTimeout implementation
 * @returns A Promise that resolves with returnValue after the specified timeout
 */
function wait<T>(timeout?: number, returnValue?: T, options?: wait.Options): Promise<T>;

namespace wait {
  interface Options {
    /** Custom setTimeout function to use instead of global setTimeout */
    readonly setTimeout?: (callback: () => void, delay: number) => any;
  }
}

Usage Examples:

import { wait } from "@hapi/hoek";

// Basic delay
await wait(1000); // Wait for 1 second
console.log('1 second has passed');

// Wait with return value
const result = await wait(2000, 'completed');
console.log(result); // 'completed' after 2 seconds

// Use in async workflows
async function processWithDelay() {
  console.log('Starting process...');
  await wait(500);
  console.log('Step 1 complete');
  await wait(1000);
  console.log('Step 2 complete');
  return 'Process finished';
}

// Timeout for rate limiting
async function rateLimitedAPI() {
  const results = [];
  for (let i = 0; i < 5; i++) {
    const data = await fetchData(i);
    results.push(data);
    await wait(200); // 200ms between requests
  }
  return results;
}

// Retry with exponential backoff
async function retryWithBackoff(operation: () => Promise<any>, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      if (attempt === maxRetries) throw error;
      
      const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
      console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
      await wait(delay);
    }
  }
}

// Simulating slow operations for testing
async function simulateSlowAPI() {
  await wait(Math.random() * 1000, 'API response');
  return { data: 'response data' };
}

// Timeout with specific return values
const timeoutResult = await wait(5000, { status: 'timeout', code: 408 });

// BigInt timeout handling
const bigIntTimeout = 1000n;
await wait(bigIntTimeout); // Automatically converted to number

// Infinite timeout (never resolves)
const neverResolving = wait(Infinity); // Promise that never resolves

// Very large timeout handling
const largeTimeout = Number.MAX_SAFE_INTEGER;
await wait(largeTimeout); // Handled safely with internal chunking

// Custom setTimeout function (for testing or special environments)
const customWait = (timeout: number, returnValue: any, options: any) => {
  return wait(timeout, returnValue, {
    setTimeout: (callback: Function, delay: number) => {
      // Custom timeout implementation
      return global.setTimeout(callback, delay);
    }
  });
};

Block

Returns a Promise that never resolves, useful for creating permanent blocking conditions.

/**
 * Returns a Promise that never resolves
 * @returns A Promise that never resolves (blocks forever)
 */
function block(): Promise<void>;

Usage Examples:

import { block } from "@hapi/hoek";

// Create a permanent block
async function waitForever() {
  console.log('Starting infinite wait...');
  await block(); // This never resolves
  console.log('This line never executes');
}

// Use in conditional blocking
async function conditionalBlock(shouldBlock: boolean) {
  if (shouldBlock) {
    await block(); // Permanently blocks execution
  }
  return 'execution continued';
}

// Server shutdown prevention
async function keepServerAlive() {
  console.log('Server is running...');
  await block(); // Keeps the process alive indefinitely
}

// Testing timeout behaviors
async function testTimeout() {
  const timeoutPromise = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('Timeout')), 5000);
  });
  
  try {
    await Promise.race([block(), timeoutPromise]);
  } catch (error) {
    console.log('Timeout occurred as expected');
  }
}

// Creating permanent background tasks
async function backgroundWorker() {
  setInterval(() => {
    console.log('Background task running...');
  }, 1000);
  
  await block(); // Keep the worker alive forever
}

// Process lifecycle management
async function gracefulShutdown() {
  process.on('SIGTERM', () => {
    console.log('Received SIGTERM, shutting down...');
    process.exit(0);
  });
  
  console.log('Application started');
  await block(); // Keep process alive until signal received
}

// Development server that never stops
async function devServer() {
  console.log('Development server starting on port 3000...');
  // Start server logic here
  await block(); // Keep server running indefinitely
}

Is Promise

Determines if an object is a promise by checking for thenable interface.

/**
 * Determines if an object is a promise
 * @param promise - The object being tested
 * @returns true if the object is a promise, otherwise false
 */
function isPromise(promise: any): boolean;

Usage Examples:

import { isPromise } from "@hapi/hoek";

// Basic promise detection
const regularPromise = Promise.resolve('value');
const notPromise = 'string';

console.log(isPromise(regularPromise)); // true
console.log(isPromise(notPromise));     // false

// Async function detection
async function asyncFunction() {
  return 'async result';
}

const asyncResult = asyncFunction(); // This is a Promise
console.log(isPromise(asyncResult)); // true

// Thenable object detection
const thenable = {
  then: (resolve: Function, reject: Function) => {
    resolve('thenable result');
  }
};

console.log(isPromise(thenable)); // true

// Function that handles both sync and async values
function processValue(value: any) {
  if (isPromise(value)) {
    return value.then((result: any) => {
      console.log('Async result:', result);
      return result;
    });
  } else {
    console.log('Sync result:', value);
    return value;
  }
}

// Usage with various value types
processValue('immediate value');
processValue(Promise.resolve('promise value'));
processValue(asyncFunction());

// Conditional awaiting
async function conditionalAwait(maybePromise: any) {
  if (isPromise(maybePromise)) {
    const result = await maybePromise;
    return `Awaited: ${result}`;
  } else {
    return `Immediate: ${maybePromise}`;
  }
}

// API response handling
function handleAPIResponse(response: any) {
  if (isPromise(response)) {
    // Handle async API response
    return response.then(data => ({ async: true, data }));
  } else {
    // Handle sync/cached response
    return { async: false, data: response };
  }
}

// Utility for mixed return types
class DataProcessor {
  process(input: any): any {
    const result = this.processSync(input);
    
    if (isPromise(result)) {
      return result.then(data => this.postProcess(data));
    } else {
      return this.postProcess(result);
    }
  }
  
  private processSync(input: any): any {
    // May return sync value or Promise based on input
    if (input.needsAsync) {
      return fetch('/api/data').then(r => r.json());
    }
    return { processed: input };
  }
  
  private postProcess(data: any) {
    return { ...data, timestamp: Date.now() };
  }
}

// Testing utilities
function createMockResponse(shouldBeAsync: boolean) {
  if (shouldBeAsync) {
    return Promise.resolve({ mocked: true });
  }
  return { mocked: true };
}

// Type checking with validation
function validatePromise(value: any): Promise<any> {
  if (!isPromise(value)) {
    throw new Error('Expected a Promise');
  }
  return value;
}

// Error handling for mixed types
async function safeDifficultProcess(value: any) {
  try {
    if (isPromise(value)) {
      return await value;
    }
    return value;
  } catch (error) {
    console.error('Error processing value:', error);
    return null;
  }
}

Important Notes:

  • wait handles various edge cases including BigInt timeouts, infinite timeouts, and very large timeout values
  • Internal timer chunking in wait prevents JavaScript timer limitations for very long timeouts
  • block is useful for keeping processes alive or creating permanent blocking conditions in specific scenarios
  • isPromise checks for thenable interface (objects with then method) rather than just Promise instances
  • All utilities work seamlessly with async/await syntax and Promise-based workflows
  • Perfect for building robust async applications with proper timing control and promise handling