Created
July 7, 2020 18:45
-
-
Save balazs-endresz/c0c814a513724f44d90265422b9ae530 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function throttleDebouncePromise( | |
inner, | |
{ throttle, debounce, cache, immediateIfCached }, | |
) { | |
// Ordinary debounce functions cause issues with promises and react-select but this works well. | |
// Based on: https://stackoverflow.com/questions/35228052/debounce-function-implemented-with-promises | |
const resolveCallbacks = [] | |
const store = {} | |
let timeout = null | |
let time = new Date() | |
return (...args) => { | |
// Return cached value immediately if exists and if immediateIfCached is true | |
if (cache && immediateIfCached) { | |
const cacheKey = getCacheKey(...args) | |
if (Object.prototype.hasOwnProperty.call(store, cacheKey)) { | |
return new Promise((resolve) => resolve(store[cacheKey])) | |
} | |
} | |
const run = () => { | |
// Resolve only the last callback if there's any left | |
const resolve = resolveCallbacks.pop() | |
if (resolve) { | |
// Remove all the previous resolve callbacks | |
resolveCallbacks.splice(0, resolveCallbacks.length) | |
// Return cached value only now if exists and if immediateIfCached is false | |
if (cache && !immediateIfCached) { | |
const cacheKey = getCacheKey(...args) | |
if (Object.prototype.hasOwnProperty.call(store, cacheKey)) { | |
return resolve(store[cacheKey]) | |
} | |
} | |
// If it wasn't cached then execute the inner function and cache when it resolves | |
const promise = inner(...args).then((result) => { | |
// Store in cache if caching is enabled | |
if (cache) { | |
store[getCacheKey(...args)] = result | |
} | |
return result | |
}) | |
resolve(promise) | |
} | |
// Reset time for throttle | |
time = new Date() | |
return undefined | |
} | |
// E.g. if a key is pressed continously this will keep making requests | |
// and NOT wait until the key is released. | |
if (throttle && new Date() - time > throttle) { | |
run() | |
} | |
// When used with throttle this only ensures the last promise is always called. | |
// Without throttle it does what a debounce function is supposed to. | |
clearTimeout(timeout) | |
timeout = setTimeout(run, debounce) | |
// Return a promise each time the final debounced function is called. | |
// This promise will just store the resolve function in `resolveCallbacks` | |
// and when it's time we'll call the last one. | |
return new Promise((resolve) => { | |
resolveCallbacks.push(resolve) | |
}) | |
} | |
} | |
function getCacheKey(...args) { | |
args.forEach((a) => { | |
if ( | |
a !== null && | |
!Number.isNaN(Number(a)) && | |
!['string', 'number', 'boolean', 'undefined'].includes(typeof a) | |
) { | |
throw Error(`Can't create cache key for ${a} : ${typeof a}`) | |
} | |
}) | |
return args.join('---') | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment