Как ускорить время с помощью поддельных таймеров Sinon? (например, при переключении вкладок и возвращении позже)

На основе Синон(ранее называвшаяся «lolex»), я не вижу API, который бы перескакивал на некоторое время вперед для кадров анимации, подобно тому, что происходит, когда мы переключаемся на другую вкладку браузера, а затем возвращаемся позже.

Как мы делаем это?

Например, когда мы переключаем вкладки, циклы requestAnimationFrame не продолжают работать. Когда мы переключимся назад, requestAnimationFrame запустится снова, но в первом кадре после переключения назад будет время, которое продвинулось вперед (перескочило) на большую величину по сравнению с обычным запуском.

Синонилибудет вести себя так, как будто вкладка постоянно находится в фокусе, и она будет работатькаждые 16,666 (поддельные) миллисекунды.

Как нам пропустить (перепрыгнуть), скажем, 1 минуту, не запуская кадров, чтобы поведение было такое, как будто мы переключили вкладки и вернулись обратно?

1 ответ

Я не думаю, что Sinon Fake Timers сможет это сделать. Вот индивидуальное решение:

      function customFakeTimers() {
    let time = Date.now();

    const OriginalDate = globalThis.Date;
    const originalRAF = globalThis.requestAnimationFrame;
    const originalCancelRAF = globalThis.cancelAnimationFrame;

    globalThis.Date = class Date {
        static now() { return time; }

        // Good enough for my cases.
        getUTCHours() { return 0; }
        getUTCMinutes() { return 1; }
        getUTCSeconds() { return 5; }
    };

    /** @type {Map<number, FrameRequestCallback>} */
    const frameFns = new Map;
    let frameId = 0;

    /**
     * @param deltaTime - Emulate 60fps by default (16.666ms per frame).
     * Provide a custom value to make rAF skip a particular amount of time,
     * for example to emulate a tab switch and returning to a tab in which
     * case a lot more time than normal would pass since the last frame.
     */
    async function toNextFrame(deltaTime = 16.6666) {
        const currentFrames = new Set(frameFns.values());
        frameFns.clear();
        await new Promise((r) => setTimeout(r, 0));
        time += deltaTime;
        for (const fn of currentFrames) { fn(time); }
        await new Promise((r) => setTimeout(r, 0));
    }

    /** @param {FrameRequestCallback} fn */
    globalThis.requestAnimationFrame = (fn) => {
        frameId++;
        frameFns.set(frameId, fn);
        return frameId;
    };

    /** @param {number} id */
    globalThis.cancelAnimationFrame = (id) => frameFns.delete(id);

    return {
        toNextFrame,

        undoCustomFakeTimers() {
            globalThis.Date = OriginalDate;
            globalThis.requestAnimationFrame = originalRAF;
            globalThis.cancelAnimationFrame = originalCancelRAF;
        },
    };
}

Чтобы использовать его,

      const {toNextFrame, undoCustomFakeTimers} = customFakeTimers()

// ...
await toNextFrame(5000) // jump 5 seconds, as if we switched tabs and came back 5 seconds later

// ... when done,
undoCustomFakeTimers()
Другие вопросы по тегам