export async function waitUntil()

in core/aws-lsp-core/src/util/timeoutUtils.ts [64:138]


export async function waitUntil<T>(fn: () => Promise<T>, options: WaitUntilOptions): Promise<T | undefined> {
    // set default opts
    const opt = {
        timeout: waitUntilDefaultTimeout,
        interval: waitUntilDefaultInterval,
        truthy: true,
        backoff: 1,
        retryOnFail: false,
        ...options,
    }

    let interval = opt.interval
    let lastError: Error | undefined
    let elapsed: number = 0
    let remaining = opt.timeout

    // Internal helper to determine if we should retry
    function shouldRetry(error: Error | undefined): boolean {
        if (error === undefined) {
            return typeof opt.retryOnFail === 'boolean' ? opt.retryOnFail : true
        }
        if (typeof opt.retryOnFail === 'function') {
            return opt.retryOnFail(error)
        }
        return opt.retryOnFail
    }

    for (let i = 0; true; i++) {
        const start: number = Date.now()
        let result: T

        try {
            // Needed in case a caller uses a 0 timeout (function is only called once)
            if (remaining > 0) {
                result = await Promise.race([fn(), new Promise<T>(r => setTimeout(r, remaining))])
            } else {
                result = await fn()
            }

            if (shouldRetry(lastError) || (opt.truthy && result) || (!opt.truthy && result !== undefined)) {
                return result
            }
        } catch (e) {
            // Unlikely to hit this, but exists for typing
            if (!(e instanceof Error)) {
                throw e
            }

            if (!shouldRetry(e)) {
                throw e
            }

            lastError = e
        }

        // Ensures that we never overrun the timeout
        remaining -= Date.now() - start

        // If the sleep will exceed the timeout, abort early
        if (elapsed + interval >= remaining) {
            if (!shouldRetry(lastError)) {
                return undefined
            }
            throw lastError
        }

        // when testing, this avoids the need to progress the stubbed clock
        if (interval > 0) {
            await sleep(interval)
        }

        elapsed += interval
        interval = interval * opt.backoff
    }
}