tldraw/packages/utils/src/lib/raf.ts
David Sheldrick d76446646c
presence-related fixes (#1361)
This fixes a bug where creating a page would fail if there were multiple
pages with the same index.

This also changes the store to use a throttled version of
requestAnimationFrame. This should be good for relieving backpressure in
situations where the store is updated many times in quick succession. It
also makes testing a lot easier since it has the mocking logic built in.

### Change Type

- [x] `patch` — Bug Fix


### Release Notes

- Fix a bug where creating a page could throw an error in some
multiplayer contexts.
2023-05-12 09:43:51 +00:00

67 lines
1.2 KiB
TypeScript

const isTest = () =>
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'test' &&
// @ts-expect-error
!globalThis.__FORCE_RAF_IN_TESTS__
const rafQueue: Array<() => void> = []
const tick = () => {
const queue = rafQueue.splice(0, rafQueue.length)
for (const fn of queue) {
fn()
}
}
let frame: number | undefined
function raf() {
if (frame) {
return
}
frame = requestAnimationFrame(() => {
frame = undefined
tick()
})
}
/**
* Returns a throttled version of the function that will only be called max once per frame.
* @param fn - the fun to return a throttled version of
* @returns
* @internal
*/
export function rafThrottle(fn: () => void) {
if (isTest()) {
return fn
}
return () => {
if (rafQueue.includes(fn)) {
return
}
rafQueue.push(fn)
raf()
}
}
/**
* Calls the function on the next frame.
* If the same fn is passed again before the next frame, it will still be called only once.
* @param fn - the fun to call on the next animation frame
* @returns
* @internal
*/
export function throttledRaf(fn: () => void) {
if (isTest()) {
return fn()
}
if (rafQueue.includes(fn)) {
return
}
rafQueue.push(fn)
raf()
}