| 43 | * @returns {Promise<void>} |
| 44 | */ |
| 45 | export async function waitForCondition<T>( |
| 46 | condition: () => Promise<T> | T, |
| 47 | timeoutMs: number, |
| 48 | errorMessage: string | (() => string), |
| 49 | intervalTimeoutMs: number = 10, |
| 50 | throwOnError: boolean = false, |
| 51 | cancelToken?: { isCancellationRequested: boolean; onCancellationRequested: Function } |
| 52 | ): Promise<NonNullable<T>> { |
| 53 | return new Promise<NonNullable<T>>(async (resolve, reject) => { |
| 54 | const disposables: IDisposable[] = []; |
| 55 | let timer: NodeJS.Timer; |
| 56 | const timerFunc = async () => { |
| 57 | if (cancelToken?.isCancellationRequested) { |
| 58 | dispose(disposables); |
| 59 | reject(new Error('Cancelled Wait Condition via cancellation token')); |
| 60 | return; |
| 61 | } |
| 62 | let success: T | undefined = undefined; |
| 63 | try { |
| 64 | const promise = condition(); |
| 65 | success = isPromise(promise) ? await promise : promise; |
| 66 | } catch (exc) { |
| 67 | if (throwOnError) { |
| 68 | dispose(disposables); |
| 69 | reject(exc); |
| 70 | } |
| 71 | } |
| 72 | if (!success) { |
| 73 | // Start up a timer again, but don't do it until after |
| 74 | // the condition is false. |
| 75 | timer = setTimeout(timerFunc, intervalTimeoutMs); |
| 76 | disposables.push({ dispose: () => clearTimeout(timer as any) }); |
| 77 | } else { |
| 78 | dispose(disposables); |
| 79 | resolve(success as NonNullable<T>); |
| 80 | } |
| 81 | }; |
| 82 | disposables.push({ dispose: () => clearTimeout(timer as any) }); |
| 83 | timer = setTimeout(timerFunc, 0); |
| 84 | if (cancelToken) { |
| 85 | cancelToken.onCancellationRequested( |
| 86 | () => { |
| 87 | dispose(disposables); |
| 88 | reject(new Error('Cancelled Wait Condition via cancellation token')); |
| 89 | return; |
| 90 | }, |
| 91 | undefined, |
| 92 | disposables |
| 93 | ); |
| 94 | } |
| 95 | const timeout = setTimeout(() => { |
| 96 | dispose(disposables); |
| 97 | errorMessage = typeof errorMessage === 'string' ? errorMessage : errorMessage(); |
| 98 | reject(new Error(errorMessage)); |
| 99 | }, timeoutMs); |
| 100 | disposables.push({ dispose: () => clearTimeout(timeout) }); |
| 101 | |
| 102 | pendingTimers.push(timer); |