@qooxdoo/framework
Version:
The JS Framework for Coders
146 lines (134 loc) • 3.55 kB
JavaScript
/**
* Class for debouncing a call, with promise support
*/
qx.Class.define("qx.tool.utils.Debounce", {
extend: qx.core.Object,
/**
* Constructor
*
* @param {Function?} fn function to call, can be null if you override `_runImpl`
* @param {Integer?} timeout the timeout
*/
construct(fn, timeout) {
super();
this.__fn = fn;
if (timeout) {
this.setTimeout(timeout);
}
},
properties: {
/** The timeout before firing the method */
timeout: {
init: 250,
nullable: false,
check: "Integer",
event: "changeTimeout"
},
/** What to do if triggered while the function is still executing:
* "ignore" means do nothing, allow the method to fire
* "restart" means to restart the timer
* "immediate" mean to allow the function to run again immediately after it stops
* "queue" means to schedule the function to run again
*/
repeatedTrigger: {
init: "ignored",
nullable: false,
check: ["ignore", "restart", "repeat", "queue"],
apply: "_applyRepeatedTrigger",
event: "changeRepeatedTrgger"
}
},
members: {
/** @type{Function} the function to call */
__fn: null,
/** @type{Boolean} that there is a repeated invocation queued */
__queuedRepeat: false,
/**
* Apply for `repeatedTrigger`
*
* @param {Boolean} value
*/
_applyQueueRepeats(value) {
if (!value && this.__queuedRepeat) {
this.__queuedRepeat = false;
}
},
/**
* Runs the function, completes when the function completes. The function returns
* whatever the callback function returned
*
* @return {var?}
*/
async run() {
let promise = this.__runPromise;
if (promise) {
let repeatedTrigger = this.getRepeatedTrigger();
if (repeatedTrigger == "restart") {
// If there is a timer id then we can restart it, otherwise it is already executing
if (this.__timerId) {
this._cancelTimer();
this._startTimer();
}
} else if (
repeatedTrigger == "queue" ||
repeatedTrigger == "immediate"
) {
this.__queuedRepeat = true;
}
return await promise;
}
if (qx.core.Environment.get("qx.debug")) {
this.assertTrue(!this.__timerId);
}
this._startTimer();
promise = this.__runPromise = new qx.Promise();
return await promise;
},
/**
* Starts the timer
*/
_startTimer() {
this.__timerId = setTimeout(() => this._onTimeout(), this.getTimeout());
},
/**
* Cancels the timer
*/
_cancelTimer() {
if (this.__timerId) {
clearTimeout(this.__timerId);
this.__timerId = null;
}
},
/**
* Called when the timeout has elapsed
*/
async _onTimeout() {
this.__timerId = null;
let promise = this.__runPromise;
try {
let result = null;
while (true) {
result = await this._runImpl();
if (this.__queuedRepeat) {
if (this.getRepeatedTrigger() == "queue") {
this._startTimer();
return;
}
} else {
break;
}
}
this.__runPromise = null;
promise.resolve(result);
} catch (ex) {
promise.reject(ex);
}
},
/**
* Called to run the actual code
*/
async _runImpl() {
await this.__fn();
}
}
});