208 lines
5.9 KiB
TypeScript
208 lines
5.9 KiB
TypeScript
/**
|
|
* @author Martin Karkowski
|
|
* @email m.karkowski@zema.de
|
|
*/
|
|
|
|
/**
|
|
* Delays some execution.
|
|
* @param delay [ms]
|
|
* @returns void
|
|
*/
|
|
export function sleep(delay: number): Promise<void> {
|
|
return new Promise<void>((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<boolean>)} 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<boolean>,
|
|
options: {
|
|
initialWait?: number;
|
|
testFirst?: boolean;
|
|
maxRetries?: number;
|
|
delay?: number;
|
|
maxTimeout?: number;
|
|
additionalDelay?: number;
|
|
} = {}
|
|
): Promise<void> {
|
|
const _options = Object.assign(
|
|
{
|
|
testFirst: true,
|
|
delay: 50,
|
|
},
|
|
options
|
|
);
|
|
|
|
const _isAsync = isAsyncFunction(testCallback);
|
|
|
|
if (_isAsync) {
|
|
return new Promise<void>(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<void>((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();
|
|
}
|
|
});
|
|
}
|
|
}
|