[d565449] | 1 | import requestAnimationFrame from '../shims/requestAnimationFrame.js';
|
---|
| 2 |
|
---|
| 3 | // Defines minimum timeout before adding a trailing call.
|
---|
| 4 | const trailingTimeout = 2;
|
---|
| 5 |
|
---|
| 6 | /**
|
---|
| 7 | * Creates a wrapper function which ensures that provided callback will be
|
---|
| 8 | * invoked only once during the specified delay period.
|
---|
| 9 | *
|
---|
| 10 | * @param {Function} callback - Function to be invoked after the delay period.
|
---|
| 11 | * @param {number} delay - Delay after which to invoke callback.
|
---|
| 12 | * @returns {Function}
|
---|
| 13 | */
|
---|
| 14 | export default function (callback, delay) {
|
---|
| 15 | let leadingCall = false,
|
---|
| 16 | trailingCall = false,
|
---|
| 17 | lastCallTime = 0;
|
---|
| 18 |
|
---|
| 19 | /**
|
---|
| 20 | * Invokes the original callback function and schedules new invocation if
|
---|
| 21 | * the "proxy" was called during current request.
|
---|
| 22 | *
|
---|
| 23 | * @returns {void}
|
---|
| 24 | */
|
---|
| 25 | function resolvePending() {
|
---|
| 26 | if (leadingCall) {
|
---|
| 27 | leadingCall = false;
|
---|
| 28 |
|
---|
| 29 | callback();
|
---|
| 30 | }
|
---|
| 31 |
|
---|
| 32 | if (trailingCall) {
|
---|
| 33 | proxy();
|
---|
| 34 | }
|
---|
| 35 | }
|
---|
| 36 |
|
---|
| 37 | /**
|
---|
| 38 | * Callback invoked after the specified delay. It will further postpone
|
---|
| 39 | * invocation of the original function delegating it to the
|
---|
| 40 | * requestAnimationFrame.
|
---|
| 41 | *
|
---|
| 42 | * @returns {void}
|
---|
| 43 | */
|
---|
| 44 | function timeoutCallback() {
|
---|
| 45 | requestAnimationFrame(resolvePending);
|
---|
| 46 | }
|
---|
| 47 |
|
---|
| 48 | /**
|
---|
| 49 | * Schedules invocation of the original function.
|
---|
| 50 | *
|
---|
| 51 | * @returns {void}
|
---|
| 52 | */
|
---|
| 53 | function proxy() {
|
---|
| 54 | const timeStamp = Date.now();
|
---|
| 55 |
|
---|
| 56 | if (leadingCall) {
|
---|
| 57 | // Reject immediately following calls.
|
---|
| 58 | if (timeStamp - lastCallTime < trailingTimeout) {
|
---|
| 59 | return;
|
---|
| 60 | }
|
---|
| 61 |
|
---|
| 62 | // Schedule new call to be in invoked when the pending one is resolved.
|
---|
| 63 | // This is important for "transitions" which never actually start
|
---|
| 64 | // immediately so there is a chance that we might miss one if change
|
---|
| 65 | // happens amids the pending invocation.
|
---|
| 66 | trailingCall = true;
|
---|
| 67 | } else {
|
---|
| 68 | leadingCall = true;
|
---|
| 69 | trailingCall = false;
|
---|
| 70 |
|
---|
| 71 | setTimeout(timeoutCallback, delay);
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | lastCallTime = timeStamp;
|
---|
| 75 | }
|
---|
| 76 |
|
---|
| 77 | return proxy;
|
---|
| 78 | }
|
---|