Skip to main content
Creates a deferred promise, exposing resolve and reject functions alongside the promise itself. This allows you to control promise resolution from outside the promise constructor.

Signature

function defer<T>(): Deferred<T>

Returns

An object containing the promise and its control functions.

Examples

Basic usage

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

const deferred = defer<number>();

// Resolve the promise from outside
deferred.resolve(42);

const result = await deferred.promise;
console.log(result); // 42

Event-based resolution

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

function waitForEvent(emitter: EventEmitter, eventName: string) {
  const deferred = defer<any>();
  
  emitter.once(eventName, (data) => {
    deferred.resolve(data);
  });
  
  emitter.once('error', (error) => {
    deferred.reject(error);
  });
  
  return deferred.promise;
}

// Usage
const data = await waitForEvent(myEmitter, 'data');

Coordinating async operations

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

class AsyncQueue<T> {
  private items: T[] = [];
  private waiting: Deferred<T>[] = [];
  
  push(item: T) {
    const waiter = this.waiting.shift();
    if (waiter) {
      waiter.resolve(item);
    } else {
      this.items.push(item);
    }
  }
  
  async pop(): Promise<T> {
    const item = this.items.shift();
    if (item !== undefined) {
      return item;
    }
    
    const deferred = defer<T>();
    this.waiting.push(deferred);
    return deferred.promise;
  }
}

const queue = new AsyncQueue<string>();

// Consumer waits for items
const consumeTask = async () => {
  const item = await queue.pop();
  console.log('Received:', item);
};

consumeTask(); // Waits

// Producer adds item
queue.push('Hello'); // Consumer receives it immediately

Timeout wrapper

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

function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
  const deferred = defer<T>();
  
  const timer = setTimeout(() => {
    deferred.reject(new Error('Timeout'));
  }, ms);
  
  promise.then(
    (value) => {
      clearTimeout(timer);
      deferred.resolve(value);
    },
    (error) => {
      clearTimeout(timer);
      deferred.reject(error);
    }
  );
  
  return deferred.promise;
}

const result = await withTimeout(fetchData(), 5000);

Manual cache implementation

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

class AsyncCache<K, V> {
  private cache = new Map<K, V>();
  private pending = new Map<K, Deferred<V>>();
  
  async get(key: K, loader: () => Promise<V>): Promise<V> {
    // Return cached value
    const cached = this.cache.get(key);
    if (cached !== undefined) {
      return cached;
    }
    
    // Wait for pending load
    const pending = this.pending.get(key);
    if (pending) {
      return pending.promise;
    }
    
    // Start new load
    const deferred = defer<V>();
    this.pending.set(key, deferred);
    
    try {
      const value = await loader();
      this.cache.set(key, value);
      deferred.resolve(value);
      return value;
    } catch (error) {
      deferred.reject(error);
      throw error;
    } finally {
      this.pending.delete(key);
    }
  }
}

const cache = new AsyncCache<string, User>();
const user = await cache.get('user-123', () => fetchUser('user-123'));

Reject example

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

const deferred = defer<string>();

// Reject the promise
deferred.reject(new Error('Something went wrong'));

try {
  await deferred.promise;
} catch (error) {
  console.error('Promise rejected:', error);
}

Build docs developers (and LLMs) love