UNPKG

p-timeout-compat

Version:
87 lines (84 loc) 2.73 kB
export class TimeoutError extends Error { name = 'TimeoutError'; constructor(message, options) { var _Error$captureStackTr; super(message, options); (_Error$captureStackTr = Error.captureStackTrace) === null || _Error$captureStackTr === void 0 || _Error$captureStackTr.call(Error, this, TimeoutError); } } const getAbortedReason = signal => { var _signal$reason; return (_signal$reason = signal.reason) !== null && _signal$reason !== void 0 ? _signal$reason : new DOMException('This operation was aborted.', 'AbortError'); }; export default function pTimeout(promise, options) { const { milliseconds, fallback, message, customTimers = { setTimeout, clearTimeout }, signal } = options; let timer; let abortHandler; const wrappedPromise = new Promise((resolve, reject) => { if (typeof milliseconds !== 'number' || Math.sign(milliseconds) !== 1) { throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``); } if (signal !== null && signal !== void 0 && signal.aborted) { reject(getAbortedReason(signal)); return; } if (signal) { abortHandler = () => { reject(getAbortedReason(signal)); }; signal.addEventListener('abort', abortHandler, { once: true }); } // Use .then() instead of async IIFE to preserve stack traces // eslint-disable-next-line promise/prefer-await-to-then, promise/prefer-catch promise.then(resolve, reject); if (milliseconds === Number.POSITIVE_INFINITY) { return; } // We create the error outside of `setTimeout` to preserve the stack trace. const timeoutError = new TimeoutError(); timer = customTimers.setTimeout(() => { if (fallback) { try { resolve(fallback()); } catch (error) { reject(error); } return; } if (typeof promise.cancel === 'function') { promise.cancel(); } if (message === false) { resolve(); } else if (message instanceof Error) { reject(message); } else { timeoutError.message = message !== null && message !== void 0 ? message : `Promise timed out after ${milliseconds} milliseconds`; reject(timeoutError); } }, milliseconds); }); // eslint-disable-next-line promise/prefer-await-to-then const cancelablePromise = wrappedPromise.finally(() => { cancelablePromise.clear(); if (abortHandler && signal) { signal.removeEventListener('abort', abortHandler); } }); cancelablePromise.clear = () => { customTimers.clearTimeout(timer); timer = undefined; }; return cancelablePromise; }