UNPKG

async-wrappers

Version:

A set of wrapper functions to perform debouncing, throttling, retrying etc.

114 lines (101 loc) 2.74 kB
import callReduce from './callReduce'; import deferred from './deferred'; /** * Options for [[debounce]] * * @typeparam Reducer an [[ArgumentsReducer]] * * @category Debounce */ /** * 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 */ var debounce = function debounce(fn) { var delay = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 50; var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var { reducer, maxDelay = 0, maxCalls, onCancel } = options; var started = 0; var 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 var 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 { var elapsed = Date.now() - started; if (elapsed >= maxDelay) { // it's been too long force execution next tick execute.defer(0); } else { var wait = Math.min(delay, maxDelay - elapsed); execute.defer(wait); } } } : () => { if (maxCalls) { calls++; if (calls >= maxCalls) { flush(); return; } } execute.defer(delay); }; var [call, runner, reject] = callReduce(fn, reducer, undefined, afterReduce); var execute = deferred(() => { calls = 0; runner()(); }); var flush = () => { calls = 0; execute.cancel(); deferred(runner()).defer(0); }; var cancel = reason => { calls = 0; execute.cancel(); if (onCancel) { onCancel(); } reject(reason ? reason : new Error('cancelled')); }; var debounced = call; debounced.cancel = cancel; debounced.flush = flush; return debounced; }; export default debounce;