cyb/src/utils/async/promise.ts

export async function waitUntil(cond: () => boolean, timeoutDuration = 60000) {
  if (cond()) {
    return true;
  }

  const waitPromise = new Promise((resolve) => {
    const interval = setInterval(() => {
      if (cond()) {
        clearInterval(interval);
        resolve(true);
      }
    }, 10);
  });

  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => {
      reject(new Error('waitUntil timed out!'));
    }, timeoutDuration);
  });

  return Promise.race([waitPromise, timeoutPromise]);
}

// eslint-disable-next-line import/no-unused-modules
export function makeCancellable<T extends (...args: any[]) => Promise<any>>(
  func: T,
  signal: AbortSignal
): (...funcArgs: Parameters<T>) => Promise<ReturnType<T>> {
  return async (...args: Parameters<T>): Promise<ReturnType<T>> => {
    // Promise that listens for the abort signal
    const abortPromise = new Promise<ReturnType<T>>((_, reject) => {
      const abortHandler = () => {
        signal.removeEventListener('abort', abortHandler); // Clean up the event listener
        reject(new DOMException('The operation was aborted.', 'AbortError'));
      };
      signal.addEventListener('abort', abortHandler, { once: true });
    });

    // Wrapping the original function in a promise
    const taskPromise = new Promise<ReturnType<T>>(async (resolve, reject) => {
      try {
        const result = await func(...args);
        resolve(result);
      } catch (error) {
        reject(error);
      }
    });

    // Using Promise.race to handle cancellation
    return Promise.race([taskPromise, abortPromise]);
  };
}

export function throwIfAborted<T extends (...args: any[]) => Promise<any>>(
  func: T,
  signal: AbortSignal
): (...funcArgs: Parameters<T>) => Promise<ReturnType<T>> {
  return async (...args: Parameters<T>): Promise<ReturnType<T>> => {
    if (signal.aborted) {
      throw new DOMException('The operation was aborted.', 'AbortError');
    }
    return func(...args);
  };
}

/**
 * Promise will be rejected after timeout.
 *
 * @param promise
 * @param timeout ms
 * @param abortController trigger abort
 * @returns
 */
// eslint-disable-next-line import/no-unused-modules
export async function withTimeout<T>(
  promise: Promise<T>,
  timeout: number,
  abortController?: AbortController
): Promise<T> {
  return Promise.race([
    promise,
    new Promise<T>((_, reject) => {
      const timer = setTimeout(() => {
        abortController?.abort('timeout');
        clearTimeout(timer);
        reject(new DOMException('timeout', 'AbortError'));
      }, timeout);
    }),
  ]);
}

Synonyms

pussy-ts/src/utils/async/promise.ts

Neighbours