session-desktop/ts/session/utils/Promise.ts

142 lines
3.3 KiB
TypeScript

type SimpleFunction<T> = (arg: T) => void;
type Return<T> = Promise<T> | T;
async function toPromise<T>(value: Return<T>): Promise<T> {
return value instanceof Promise ? value : Promise.resolve(value);
}
/**
* Create a promise which waits until `done` is called or until `timeout` period is reached.
* If `timeout` is reached then this will throw an Error.
*
* @param task The task to wait for.
* @param timeout The timeout period.
*/
export async function waitForTask<T>(
task: (done: SimpleFunction<T>) => Return<void>,
timeoutMs: number = 2000
): Promise<T> {
const timeoutPromise = new Promise<T>((_, rej) => {
const wait = setTimeout(() => {
clearTimeout(wait);
rej(new Error('Task timed out.'));
}, timeoutMs);
});
const taskPromise = new Promise(async (res, rej) => {
try {
await toPromise(task(res));
} catch (e) {
rej(e);
}
});
return Promise.race([timeoutPromise, taskPromise]) as Promise<T>;
}
export interface PollOptions {
timeoutMs: number;
interval: number;
}
/**
* Creates a promise which calls the `task` every `interval` until `done` is called or until `timeout` period is reached.
* If `timeout` is reached then this will throw an Error.
*
* @param task The task which runs every `interval` ms.
* @param options The polling options.
*/
export async function poll(
task: (done: SimpleFunction<void>) => Return<void>,
options: Partial<PollOptions> = {}
): Promise<void> {
const defaults: PollOptions = {
timeoutMs: 2000,
interval: 100,
};
const { timeoutMs, interval } = {
...defaults,
...options,
};
const endTime = Date.now() + timeoutMs;
let stop = false;
const finish = () => {
stop = true;
};
const _poll = async (resolve: any, reject: any) => {
if (stop) {
resolve();
} else if (Date.now() >= endTime) {
finish();
reject(new Error('Periodic check timeout'));
} else {
try {
await toPromise(task(finish));
} catch (e) {
finish();
reject(e);
return;
}
setTimeout(() => {
void _poll(resolve, reject);
}, interval);
}
};
return new Promise((resolve, reject) => {
void _poll(resolve, reject);
});
}
/**
* Creates a promise which waits until `check` returns `true` or rejects if `timeout` preiod is reached.
* If `timeout` is reached then this will throw an Error.
*
* @param check The boolean check.
* @param timeout The time before an error is thrown.
*/
export async function waitUntil(
check: () => Return<boolean>,
timeoutMs: number = 2000
) {
// This is causing unhandled promise rejection somewhere in MessageQueue tests
return poll(
async done => {
const result = await toPromise(check());
if (result) {
done();
}
},
{
timeoutMs,
interval: timeoutMs / 20,
}
);
}
export async function timeout<T>(
promise: Promise<T>,
timeoutMs: number = 2000
): Promise<T> {
const timeoutPromise = new Promise<T>((_, rej) => {
const wait = setTimeout(() => {
clearTimeout(wait);
rej(new Error('Task timed out.'));
}, timeoutMs);
});
return Promise.race([timeoutPromise, promise]);
}
export async function delay(timeoutMs: number = 2000): Promise<Boolean> {
return new Promise(resolve => {
setTimeout(() => {
resolve(true);
}, timeoutMs);
});
}