From 496c6c697da037e76071c6deffd7c66ce5a42347 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 6 Sep 2024 15:42:27 +0200 Subject: [PATCH] [Perf] Batch `requestIdleCallback` calls --- src/hooks/useHeavyAnimationCheck.ts | 6 ++-- src/util/schedulers.ts | 49 +++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/hooks/useHeavyAnimationCheck.ts b/src/hooks/useHeavyAnimationCheck.ts index a64c4d0c4..b7b97d5d7 100644 --- a/src/hooks/useHeavyAnimationCheck.ts +++ b/src/hooks/useHeavyAnimationCheck.ts @@ -105,14 +105,14 @@ export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) { return onEnd; } -export function onFullyIdle(cb: NoneToVoidFunction, idleTimeout?: number) { +export function onFullyIdle(cb: NoneToVoidFunction) { onIdle(() => { if (getIsAnimating()) { requestMeasure(() => { - onFullyIdle(cb, idleTimeout); + onFullyIdle(cb); }); } else { cb(); } - }, idleTimeout); + }); } diff --git a/src/util/schedulers.ts b/src/util/schedulers.ts index 8a4e22ee1..09b075b30 100644 --- a/src/util/schedulers.ts +++ b/src/util/schedulers.ts @@ -84,16 +84,6 @@ export function throttleWith(schedulerFn: Scheduler }; } -export function onIdle(cb: NoneToVoidFunction, timeout?: number) { - // eslint-disable-next-line no-restricted-globals - if (self.requestIdleCallback) { - // eslint-disable-next-line no-restricted-globals - self.requestIdleCallback(cb, { timeout }); - } else { - onTickEnd(cb); - } -} - export const pause = (ms: number) => new Promise((resolve) => { setTimeout(() => resolve(), ms); }); @@ -176,6 +166,45 @@ export function onTickEnd(callback: NoneToVoidFunction) { } } +const IDLE_TIMEOUT = 500; + +let onIdleCallbacks: NoneToVoidFunction[] | undefined; + +export function onIdle(callback: NoneToVoidFunction) { + // eslint-disable-next-line no-restricted-globals + if (!self.requestIdleCallback) { + onTickEnd(callback); + return; + } + + if (!onIdleCallbacks) { + onIdleCallbacks = [callback]; + + requestIdleCallback((deadline) => { + const currentCallbacks = onIdleCallbacks!; + onIdleCallbacks = undefined; + + while (currentCallbacks.length) { + const cb = currentCallbacks.shift()!; + cb(); + + if (!deadline.timeRemaining()) break; + } + + if (currentCallbacks.length) { + if (onIdleCallbacks) { + // Prepend the remaining callbacks if the next pass is already planned + onIdleCallbacks = currentCallbacks.concat(onIdleCallbacks); + } else { + currentCallbacks.forEach(onIdle); + } + } + }, { timeout: IDLE_TIMEOUT }); + } else { + onIdleCallbacks.push(callback); + } +} + let beforeUnloadCallbacks: NoneToVoidFunction[] | undefined; export function onBeforeUnload(callback: NoneToVoidFunction, isLast = false) {