All files / owid-grapher/clientUtils PromiseSwitcher.ts

93.1% Statements 54/58
60% Branches 3/5
100% Functions 3/3
93.1% Lines 54/58

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 531x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 8x 8x 8x 1x 1x 2x 2x 2x 2x 2x 2x 2x         2x 1x 1x 2x 2x 1x 1x
interface PromiseSelectorArg<Result> {
    onResolve?: (result: Result) => void
    onReject?: (error: any) => void
}
 
/**
 * An alternative to cancellable promises. Allows set()-ing a single promise,
 * discarding the results of any previous ones, regardless whether they
 * resolve or reject.
 *
 * The problem it solves:
 *
 * 1. User interacts with UI
 * 2. Request sent to fetch required data: `getViewData().then((data) => updateView(data))`
 * 3. But now what if the user navigates away? When the promise completes, it will
 *    update the view regardless.
 *
 * In order to solve this, we use `switcher = PromiseSwitcher({ onResolve: updateView })`
 * And then send off requests with `switcher.set(getViewData())`
 * If set() is called with a new Promise while the previous is still pending, the
 * pending promise is ignored – it doesn't call `onResolve` or `onReject`.
 *
 */
export class PromiseSwitcher<Result> {
    private pendingPromise: Promise<Result> | undefined
 
    private onResolve?: (result: Result) => void
    private onReject?: (error: any) => void
 
    constructor(arg: PromiseSelectorArg<Result>) {
        this.onResolve = arg.onResolve
        this.onReject = arg.onReject
    }
 
    async set(promise: Promise<Result>): Promise<void> {
        this.pendingPromise = promise
        try {
            const result = await promise
            if (this.pendingPromise === promise) {
                this.onResolve?.(result)
            }
        } catch (error) {
            if (this.pendingPromise === promise) {
                this.onReject?.(error)
            }
        }
    }
 
    clear(): void {
        this.pendingPromise = undefined
    }
}