async-wrappers
Version:
A set of wrapper functions to perform debouncing, throttling, retrying etc.
79 lines (66 loc) • 1.88 kB
JavaScript
import callReduce from './callReduce';
import deferred from './deferred';
/**
* Ensure multiple calls to a function will only execute at most once every
* `delay` milliseconds.
*
* The execution will always happens asynchronously. If the delay is `0` then
* execution will occur after the current runtime event loop.
*
* The throttled function returns a promise that resolves to the return value
* of `func`.
*
* By default the arguments to `func` are the latest arguments given to the
* throttled function. For custom behaviour pass an `argumentsReducer`
* function.
*
* @param func The function to throttle
* @param delay The number of milliseconds between executions.
* @param options Debounce options
* @return The throttled function
*
* @category Wrapper
*/
const throttle = (func, delay = 50, options = {}) => {
const {
reducer,
onCancel,
maxCalls
} = options;
let calls = 0;
const afterReduce = () => {
if (maxCalls) {
calls++;
if (calls >= maxCalls) {
flush();
return;
}
} // nothing pending so go
if (execute.delay < 0) {
const elapsed = execute.called > 0 ? Date.now() - execute.called : delay; // call right away or with how much time to go before next call
execute.defer(elapsed >= delay ? 0 : delay - elapsed);
}
};
const [call, runner, reject] = callReduce(func, reducer, undefined, afterReduce);
const execute = deferred(() => {
calls = 0;
runner()();
});
const flush = () => {
calls = 0;
execute.cancel();
deferred(runner()).defer(0);
};
const cancel = reason => {
execute.cancel();
if (onCancel) {
onCancel();
}
reject(reason ? reason : new Error('cancelled'));
};
const wrapped = call;
wrapped.cancel = cancel;
wrapped.flush = flush;
return wrapped;
};
export default throttle;