Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor(lazy): add interval variable to simplify
Signed-off-by: Max <[email protected]>
  • Loading branch information
max-nextcloud committed Feb 18, 2023
commit 8b662bd71137dc181671d012aece2fbdbac46d4f
71 changes: 8 additions & 63 deletions src/helpers/lazy.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,80 +48,25 @@ export { lazyTimer }
*
* @param {Function} inner function to be called
* @param {object} options optional
* @param {number} options.skipAtMost maximum number of calls to skip, default: 15
* @param {number} options.maxInterval maximum interval between two calls to inner
*/
export function lazy(inner, { skipAtMost = 15 } = {}) {
export function lazy(inner, { maxInterval = 16 } = {}) {
let count = 0
let interval = 1
const result = (...args) => {
count++
if (runFor(count, skipAtMost)) {
if (count === interval) {
count = 0
interval = Math.min(interval * 2, maxInterval)
return inner(...args)
}
}
result.wakeUp = () => {
count = 0
interval = 1
}
result.sleep = () => {
const previousRun = runsBefore(count)
const previousCount = countAt(previousRun)
count = lastCountAfterDoubling(skipAtMost) + count - previousCount
interval = maxInterval
}
return result
}

/**
* @param {number} count time the function is being called
* @param {number} skipAtMost maximum number of calls to skip
*/
function runFor(count, skipAtMost) {
const nextRun = runsBefore(count) + 1
const skips = skipsBefore(nextRun)
if (!skipAtMost || skips < skipAtMost) {
return count === countAt(nextRun)
} else {
const runEvery = skipAtMost + 1
const result = (count - lastCountAfterDoubling(skipAtMost)) % runEvery === 0
return result
}
}

/**
* At what count does the inner function run for the nth time.
*
* @param {number} n time the inner function runs
* @return {number}
*/
function countAt(n) {
return 2 ** n - 1
}

/**
* How many runs happened before count.
*
* @param {number} count time the lazy function is being called
* @return {number}
*/
function runsBefore(count) {
return Math.floor(Math.log2(count))
}

/**
* How many calls of the lazy function are skipped before it runs the nth time.
*
* @param {number} n time the inner function runs
* @return {number}
*/
function skipsBefore(n) {
return (n === 1) ? 1 : countAt(n - 1)
}

/**
* Count when the limit to doubling the intervals was reached.
*
* @param {number} skipAtMost upper limit for doubling
* @return {number}
*/
function lastCountAfterDoubling(skipAtMost) {
const lastRunToDoubleAfter = Math.floor(Math.log2(skipAtMost + 1))
return countAt(lastRunToDoubleAfter + 1)
}
8 changes: 4 additions & 4 deletions src/tests/helpers/lazy.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,22 +114,22 @@ describe('lazy function', () => {

test('respects skipAtMost option', () => {
const inner = jest.fn()
const fn = lazy(inner, { skipAtMost: 3 })
const fn = lazy(inner, { maxInterval: 4 })
callNTimes(20, fn)
expect(inner.mock.calls.map(call => call[0])).toEqual([1,3,7,11,15,19])
})

test('skipAtMost defaults to 15', () => {
test('maxInterval defaults to 16', () => {
const inner = jest.fn()
const fn = lazy(inner)
callNTimes(64, fn)
expect(inner.mock.calls.map(call => call[0])).toEqual([1,3,7,15,31,47,63])
})

test('skips skipAtMost after sleep was called', () => {
test('Uses maxInterval after sleep was called', () => {
const inner = jest.fn()
let count = 0
const lazyFn = lazy(() => inner(count), { skipAtMost: 5 })
const lazyFn = lazy(() => inner(count), { maxInterval: 6 })
const trigger = () => {
count++
lazyFn()
Expand Down