async-wrappers
Version:
A set of wrapper functions to perform debouncing, throttling, retrying etc.
116 lines (99 loc) • 2.82 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _callReduce = _interopRequireDefault(require("./callReduce"));
var _deferred = _interopRequireDefault(require("./deferred"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Ensure multiple calls to a function will only execute it when it has been
* inactive (no more calls) for a specified delay.
*
* Execution always happens asynchronously.
*
* If the delay is `0` execution will be scheduled to occur after the current
* runtime event loop.
*
* The debounced 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
* debounced function. For custom behaviour pass an `argumentsReducer`
* function.
*
* @param fn The function to debounce
* @param delay The number of milliseconds on inactivity before the function
* will be executed.
.
* @return the debounced function
*
* @category Wrapper
*/
const debounce = (fn, delay = 50, options = {}) => {
const {
reducer,
maxDelay = 0,
maxCalls,
onCancel
} = options;
let started = 0;
let calls = 0; // Micro optimization if delay is zero defer will always force execution next tick so we can ignore maxDelay
// as all calls this process tick will be handled next tick
const afterReduce = delay > 0 && maxDelay > 0 ? () => {
if (maxCalls) {
calls++;
if (calls >= maxCalls) {
flush();
return;
}
}
if (execute.delay < 0) {
// execute is fresh reset started
started = Date.now();
execute.defer(Math.min(delay, maxDelay));
} else {
const elapsed = Date.now() - started;
if (elapsed >= maxDelay) {
// it's been too long force execution next tick
execute.defer(0);
} else {
const wait = Math.min(delay, maxDelay - elapsed);
execute.defer(wait);
}
}
} : () => {
if (maxCalls) {
calls++;
if (calls >= maxCalls) {
flush();
return;
}
}
execute.defer(delay);
};
const [call, runner, reject] = (0, _callReduce.default)(fn, reducer, undefined, afterReduce);
const execute = (0, _deferred.default)(() => {
calls = 0;
runner()();
});
const flush = () => {
calls = 0;
execute.cancel();
(0, _deferred.default)(runner()).defer(0);
};
const cancel = reason => {
calls = 0;
execute.cancel();
if (onCancel) {
onCancel();
}
reject(reason ? reason : new Error('cancelled'));
};
const debounced = call;
debounced.cancel = cancel;
debounced.flush = flush;
return debounced;
};
var _default = debounce;
exports.default = _default;