export default class Debouncer {
constructor(callback) {
console.assert(typeof callback === "function", callback);
this._callback = callback;
this._lastArguments = [];
this._timeoutIdentifier = undefined;
this._animationFrameIdentifier = undefined;
this._promiseIdentifier = undefined;
this._resultPromise = null;
this._delayedIdentifier = undefined;
}
// Public
get active() {
return this._timeoutIdentifier !== undefined || this._animationFrameIdentifier !== undefined || this._promiseIdentifier !== undefined;
}
delayForTime(time, ...args) {
console.assert(time >= 0, time);
this._lastArguments = args;
this._awaitResultPromiseIfNeeded(() => {
this._cancelDelay();
this._timeoutIdentifier = setTimeout(() => {
this._execute();
}, time);
});
}
delayForFrame(...args) {
this._lastArguments = args;
this._awaitResultPromiseIfNeeded(() => {
this._cancelDelay();
this._animationFrameIdentifier = requestAnimationFrame(() => {
this._execute();
});
});
}
delayForMicrotask(...args) {
this._lastArguments = args;
this._awaitResultPromiseIfNeeded(() => {
this._cancelDelay();
let promiseIdentifier = Symbol();
this._promiseIdentifier = promiseIdentifier;
queueMicrotask(() => {
if (this._promiseIdentifier === promiseIdentifier)
this._execute();
});
});
}
force(...args) {
this._lastArguments = args;
this._execute();
}
cancel() {
this._lastArguments = [];
this._cancelDelay();
this._resultPromise = null;
this._delayedIdentifier = undefined;
}
// Private
_cancelDelay() {
if (this._timeoutIdentifier) {
clearTimeout(this._timeoutIdentifier);
this._timeoutIdentifier = undefined;
}
if (this._animationFrameIdentifier) {
cancelAnimationFrame(this._animationFrameIdentifier);
this._animationFrameIdentifier = undefined;
}
this._promiseIdentifier = undefined;
}
_awaitResultPromiseIfNeeded(callback) {
if (this._resultPromise || this._delayedIdentifier) {
if (!this._delayedIdentifier) {
let delayedIdentifier = Symbol();
this._delayedIdentifier = delayedIdentifier;
this._resultPromise.finally(() => {
if (this._delayedIdentifier !== delayedIdentifier)
return;
callback();
});
}
return;
}
callback();
}
_execute() {
let args = this._lastArguments;
this.cancel();
let result = undefined;
try {
result = this._callback.apply(undefined, args);
} catch { }
if (result instanceof Promise) {
this._resultPromise = result;
this._resultPromise.finally(() => {
this._resultPromise = null;
});
}
}
}