Skip to main content
Waits until a predicate returns true, polling at a configurable interval. Useful for waiting for conditions to be met.

Signature

async function wait(
  predicate: () => Promise<boolean> | boolean,
  options?: StandardOptions & {
    interval?: number;
    timeout?: number;
  },
): Promise<void>

Parameters

predicate
() => Promise<boolean> | boolean
required
The condition to wait for. Called repeatedly until it returns true.
options
StandardOptions & { interval?: number; timeout?: number }
Wait options.

Returns

A promise that resolves when the predicate returns true.

Throws

  • TimeoutError if the timeout is exceeded
  • AbortError if the signal is aborted
  • Any error thrown by the predicate

Examples

Basic usage

import { wait } from '@temelj/async';

let ready = false;

setTimeout(() => {
  ready = true;
}, 1000);

// Wait until ready is true
await wait(() => ready);
console.log('Ready!');

Wait for element to appear

import { wait } from '@temelj/async';

// Wait for DOM element
await wait(
  () => document.querySelector('#my-element') !== null,
  { interval: 50 }
);

const element = document.querySelector('#my-element');

Wait for API to be ready

import { wait } from '@temelj/async';

async function waitForAPI() {
  await wait(
    async () => {
      try {
        const response = await fetch('/api/health');
        return response.ok;
      } catch {
        return false;
      }
    },
    { interval: 1000, timeout: 30000 } // Check every second, timeout after 30s
  );
  
  console.log('API is ready');
}

Wait for file to exist

import { wait } from '@temelj/async';
import { access } from 'fs/promises';

async function waitForFile(path: string) {
  await wait(
    async () => {
      try {
        await access(path);
        return true;
      } catch {
        return false;
      }
    },
    { interval: 500, timeout: 10000 }
  );
}

await waitForFile('./output.txt');
console.log('File exists!');

Wait for condition with timeout

import { wait } from '@temelj/async';

let counter = 0;

const interval = setInterval(() => {
  counter++;
}, 100);

try {
  await wait(
    () => counter >= 10,
    { interval: 50, timeout: 2000 }
  );
  console.log('Condition met!');
} catch (error) {
  if (error instanceof TimeoutError) {
    console.error('Timed out waiting for condition');
  }
} finally {
  clearInterval(interval);
}

Wait for resource to be available

import { wait } from '@temelj/async';

class ResourcePool {
  private available = 0;
  
  async acquire() {
    await wait(
      () => this.available > 0,
      { interval: 10, timeout: 5000 }
    );
    
    this.available--;
    return () => this.available++; // release function
  }
  
  release() {
    this.available++;
  }
}

Database connection ready

import { wait } from '@temelj/async';

class Database {
  private connected = false;
  
  async connect() {
    // Start connection process
    this.startConnection();
    
    // Wait for connection to be established
    await wait(
      () => this.connected,
      { interval: 100, timeout: 10000 }
    );
    
    console.log('Database connected');
  }
  
  private startConnection() {
    setTimeout(() => {
      this.connected = true;
    }, 2000);
  }
}

With abort signal

import { wait } from '@temelj/async';

const controller = new AbortController();

const task = wait(
  () => false, // Never true
  { 
    interval: 100,
    signal: controller.signal 
  }
);

// Cancel after 1 second
setTimeout(() => controller.abort(), 1000);

try {
  await task;
} catch (error) {
  if (error instanceof AbortError) {
    console.log('Wait cancelled');
  }
}

Wait for multiple conditions

import { wait } from '@temelj/async';

let serviceA = false;
let serviceB = false;
let serviceC = false;

// Start services
startServices();

// Wait for all services to be ready
await wait(
  () => serviceA && serviceB && serviceC,
  { interval: 200, timeout: 30000 }
);

console.log('All services ready');

Polling with custom interval

import { wait } from '@temelj/async';

async function waitForJobCompletion(jobId: string) {
  await wait(
    async () => {
      const status = await checkJobStatus(jobId);
      return status === 'completed';
    },
    { interval: 2000 } // Check every 2 seconds
  );
  
  return await getJobResult(jobId);
}

const result = await waitForJobCompletion('job-123');

Wait for state change

import { wait } from '@temelj/async';

class StateMachine {
  private state = 'idle';
  
  async waitForState(targetState: string, timeout?: number) {
    await wait(
      () => this.state === targetState,
      { interval: 50, timeout }
    );
  }
  
  setState(newState: string) {
    this.state = newState;
  }
}

const machine = new StateMachine();

setTimeout(() => machine.setState('ready'), 1000);

await machine.waitForState('ready', 5000);
console.log('State machine is ready');

Wait with progress reporting

import { wait } from '@temelj/async';

async function waitWithProgress(
  predicate: () => boolean | Promise<boolean>,
  maxTime: number
) {
  const startTime = Date.now();
  
  await wait(
    async () => {
      const elapsed = Date.now() - startTime;
      const progress = Math.min((elapsed / maxTime) * 100, 100);
      console.log(`Progress: ${progress.toFixed(0)}%`);
      return predicate();
    },
    { interval: 500, timeout: maxTime }
  );
}

let done = false;
setTimeout(() => done = true, 3000);

await waitWithProgress(() => done, 5000);

Build docs developers (and LLMs) love