export default class Throttler {
constructor(callback, delay) {
console.assert(typeof callback === "function", callback);
console.assert(delay >= 0, delay);
this._callback = callback;
this._delay = delay;
this._lastArguments = [];
this._timeoutIdentifier = undefined;
this._lastFireTime = -this._delay;
this._resultPromise = null;
this._delayedIdentifier = undefined;
}
// Public
get active() {
return this._timeoutIdentifier !== undefined;
}
fire(...args) {
this._lastArguments = args;
this._awaitResultPromiseIfNeeded(() => {
let remaining = this._delay - (Date.now() - this._lastFireTime);
if (remaining <= 0) {
this._execute();
return;
}
if (this._timeoutIdentifier)
return;
this._timeoutIdentifier = setTimeout(() => {
this._execute();
}, remaining);
});
}
force(...args) {
this._lastArguments = args;
this._execute();
}
cancel() {
this._lastArguments = [];
if (this._timeoutIdentifier) {
clearTimeout(this._timeoutIdentifier);
this._timeoutIdentifier = undefined;
}
this._resultPromise = null;
this._delayedIdentifier = undefined;
}
// Private
_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() {
this._lastFireTime = Date.now();
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;
});
}
}
}