import requestAnimationFrame from '../shims/requestAnimationFrame.js';
|
|
// Defines minimum timeout before adding a trailing call.
|
const trailingTimeout = 2;
|
|
/**
|
* Creates a wrapper function which ensures that provided callback will be
|
* invoked only once during the specified delay period.
|
*
|
* @param {Function} callback - Function to be invoked after the delay period.
|
* @param {number} delay - Delay after which to invoke callback.
|
* @returns {Function}
|
*/
|
export default function (callback, delay) {
|
let leadingCall = false,
|
trailingCall = false,
|
lastCallTime = 0;
|
|
/**
|
* Invokes the original callback function and schedules new invocation if
|
* the "proxy" was called during current request.
|
*
|
* @returns {void}
|
*/
|
function resolvePending() {
|
if (leadingCall) {
|
leadingCall = false;
|
|
callback();
|
}
|
|
if (trailingCall) {
|
proxy();
|
}
|
}
|
|
/**
|
* Callback invoked after the specified delay. It will further postpone
|
* invocation of the original function delegating it to the
|
* requestAnimationFrame.
|
*
|
* @returns {void}
|
*/
|
function timeoutCallback() {
|
requestAnimationFrame(resolvePending);
|
}
|
|
/**
|
* Schedules invocation of the original function.
|
*
|
* @returns {void}
|
*/
|
function proxy() {
|
const timeStamp = Date.now();
|
|
if (leadingCall) {
|
// Reject immediately following calls.
|
if (timeStamp - lastCallTime < trailingTimeout) {
|
return;
|
}
|
|
// Schedule new call to be in invoked when the pending one is resolved.
|
// This is important for "transitions" which never actually start
|
// immediately so there is a chance that we might miss one if change
|
// happens amids the pending invocation.
|
trailingCall = true;
|
} else {
|
leadingCall = true;
|
trailingCall = false;
|
|
setTimeout(timeoutCallback, delay);
|
}
|
|
lastCallTime = timeStamp;
|
}
|
|
return proxy;
|
}
|