SimenB/wait-until-promise

View on GitHub
waitUntilPromise.js

Summary

Maintainability
A
0 mins
Test Coverage
let PromiseImplementation;

try {
  PromiseImplementation = Promise;
} catch (err) {
  // ignored
}

/**
 * Clears the specified timeout and interval.
 *
 * @param {number} timeout - Id if the timeout to clear.
 * @param {number} interval - Id of the interval to clear.
 * @private
 */
function clearTimers(timeout, interval) {
  clearTimeout(timeout);
  clearInterval(interval);
}

/**
 * Set a custom {@link Promise} implementation.
 *
 * @param {Function} implementation - A promise implementation to use instead of native {@link Promise}.
 * @static
 */
export const setPromiseImplementation = implementation => {
  PromiseImplementation = implementation;
};

/**
 * Create a {@link Promise} that resolves if the given escapeFunction returns a truthy value, and rejects if it throws
 * or does not return truthy within the given maxWait.
 *
 * @param {Function} escapeFunction - The function called every checkDelay, and the result of which is the resolved
 * value of the promise.
 * @param {number} maxWait - The time to wait before rejecting the promise.
 * @param {number} checkDelay - The time to wait before each invocation of {escapeFunction}.
 * @returns {Promise} A promise resolved with the value of escapeFunction, or rejected with the exception thrown by it
 * or it times out.
 */
export default (escapeFunction, maxWait = 50, checkDelay = 1) => {
  if (PromiseImplementation == null) {
    throw new Error('Wait Until Promise: No global Promise available, make sure to use `setPromiseImplementation`.');
  }

  // Run the function once without setting up any listeners in case it's already true
  try {
    const escapeFunctionRes = escapeFunction();

    if (escapeFunctionRes) return PromiseImplementation.resolve(escapeFunctionRes);
  } catch (e) {
    return PromiseImplementation.reject(e);
  }

  return new PromiseImplementation((resolve, reject) => {
    let maxWaitTimeout;

    const interval = setInterval(() => {
      try {
        const escapeFunctionRes = escapeFunction();

        if (escapeFunctionRes) {
          clearTimers(maxWaitTimeout, interval);

          resolve(escapeFunctionRes);
        }
      } catch (e) {
        clearTimers(maxWaitTimeout, interval);

        reject(e);
      }
    }, checkDelay);

    maxWaitTimeout = setTimeout(() => {
      clearTimers(maxWaitTimeout, interval);

      // Try to reject with a TimeoutError, like Bluebird has
      if (PromiseImplementation.TimeoutError) {
        reject(new PromiseImplementation.TimeoutError('Wait until promise timed out'));
      } else {
        reject(new Error('Wait until promise timed out'));
      }
    }, maxWait);
  });
};