/** * @author Martin Karkowski * @email m.karkowski@zema.de */ /** * Delays some execution. * @param delay [ms] * @returns void */ export function sleep(delay: number): Promise { return new Promise((resolve) => { setTimeout(resolve, delay); }); } /** * Tests if a Function is async or not. * @param func * @returns */ export function isAsyncFunction(func: (...args) => any): boolean { return func.constructor.name === "AsyncFunction"; } /** * Function which will halt the Process until the Testcallback deliveres "true" * * @export * @param {(() => boolean | Promise)} testCallback Function which is used to periodically test the State * @param {{ testFirst?: boolean; maxRetries?: number, timeout?: number, maxTimeout?: number }} [options={}] Options to enhance the * @returns */ export function waitFor( testCallback: () => boolean | Promise, options: { initialWait?: number; testFirst?: boolean; maxRetries?: number; delay?: number; maxTimeout?: number; additionalDelay?: number; } = {} ): Promise { const _options = Object.assign( { testFirst: true, delay: 50, }, options ); const _isAsync = isAsyncFunction(testCallback); if (_isAsync) { return new Promise(async (resolve, reject) => { if (options.initialWait) { await sleep(options.initialWait); } let _resolve: () => void; if (_options.additionalDelay) { _resolve = () => { setTimeout(resolve, _options.additionalDelay); }; } else { _resolve = resolve; } try { if (_options.testFirst && (await testCallback())) { _resolve(); } else { let retryCounter = 0; let timeout: any | null = null; let interval: any | null = null; // If there is a Timeout, define a Timeout Function, which will // Throw an Error on Timeout. if (_options.maxTimeout) { timeout = setTimeout(async () => { if (interval) { clearInterval(interval); } reject(new Error("Wait has been Timeout")); }, _options.maxTimeout); } // Define a Testfunction, which will periodically test whether the condition is // fullfield or not. Internally it counts the number of retries, if the max allowed // number of retries has been reached => Throw an Error interval = setInterval(async () => { try { if (_options.maxRetries && retryCounter > _options.maxRetries) { // Clear out the Interval clearInterval(interval); // If there is a Timeout clear it as well; if (timeout) { clearTimeout(timeout); } reject(new RangeError("Max Retries has been reached")); } else if (await testCallback()) { // Clear out the Interval clearInterval(interval); // If there is a Timeout clear it as well; if (timeout) { clearTimeout(timeout); } _resolve(); } retryCounter += 1; } catch (err) { reject(err); } }, _options.delay); } } catch (e) { reject(e); } }); } else { return new Promise((resolve, reject) => { const _func = () => { let _resolve: () => void; if (_options.additionalDelay) { _resolve = () => { setTimeout(resolve, _options.additionalDelay); }; } else { _resolve = resolve; } try { if (_options.testFirst && testCallback()) { if (_options.additionalDelay) { setTimeout(resolve, _options.additionalDelay); } else { _resolve(); } } else { let retryCounter = 0; let timeout: any | null = null; let interval: any | null = null; // If there is a Timeout, define a Timeout Function, which will // Throw an Error on Timeout. if (_options.maxTimeout) { timeout = setTimeout(async () => { if (interval) { clearInterval(interval); } reject(new Error("Wait has been Timeout")); }, _options.maxTimeout); } // Define a Testfunction, which will periodically test whether the condition is // fullfield or not. Internally it counts the number of retries, if the max allowed // number of retries has been reached => Throw an Error interval = setInterval(() => { try { if (_options.maxRetries && retryCounter > _options.maxRetries) { // Clear out the Interval clearInterval(interval); // If there is a Timeout clear it as well; if (timeout) { clearTimeout(timeout); } reject(new RangeError("Max Retries has been reached")); } else if (testCallback()) { // Clear out the Interval clearInterval(interval); // If there is a Timeout clear it as well; if (timeout) { clearTimeout(timeout); } _resolve(); } retryCounter += 1; } catch (err) { reject(err); } }, _options.delay); } } catch (e) { reject(e); } }; if (options.initialWait) { setTimeout(_func, options.initialWait); } else { _func(); } }); } }