Event rate limiter
It is important to use the correct terms when defining functions. If you are unsure it is always better to look up the literature to ensure you are using the term correctly.
Debouncing
You have not implemented a de-bounce. De-bounce delays action on an event to prevent it firing in rapid succession.
The term comes from ye old day hardware where the mechanical switch would have a tendency to bounce on the contacts, giving a series of on/off signals when only one was intended.
In Javascript we use de-bounce to prevent events that fire at high rates (eg mouse resize) from firing at a rate higher than is useful, (eg screen refresh rate.)
You could implement a de-bounce as follows
const debounce = {
debounceTime : 33, // in ms
timeoutHandle : undefined,
ondebounced(event) {
log("Resized");
},
event(event) {
clearTimeout(debounce.timeoutHandle);
debounce.timeoutHandle = setTimeout(debounce.ondebounced, debounce.debounceTime, event)
}
}
addEventListener("resize",debounce.event);
It will wait 33ms after the last resize event before calling the de-bounced function.
Rate limiter
You have implemented a rate limiter, that forces an event to fire at a set maximum rate. The difference are minor,
- De-bounce fires after the events (signal noise) while a rate limiter fire as soon as possible.
- De-bounce delay is indeterminate, while rate limiting has a fixed determinate delay.
Your code
Your code is a little old school. You should keep up with the lates JS version ES8.
- You should be using
const
when you intended variables not to change.
- Use arrow functions.
- Use the spread / rest tokens for passing unknown arguments. See example
You also are mixing a variable's use. The var timeout
. This may not cause you trouble at the moment and seem like a good idea to reduce code size by doubling a variables meaning, (means timeoutHandle
, and Block events) But will bite if in future you make changes and forget that timeout
is more than just a handle.
Never mix a variables meaning without making it clear in the name
Example
The following is an example of how you could implement a rate limiter.
The functionality is encapsulated and access is only via getters and setters to prevent miss use. See comments for more. This is only an example, not a definitive how too.
// Encapsulated rateLimit event utility
const rateLimit = (()=>{
const rate = 2000; // the max rate in ms
// the limiting functions, pass the event to call
// returns an API for handling the events.
return function (eventFunc){
var handle; // for unblock event
var blocked = false; // true if blocked
var event; // the event to call if not blocked
// this function handles the incoming event
const eventHandler = (...eventArgs) => {
if(blocked){
log("Event blocked. Too fast.")
}else{
API.block = true;
if (event) { event(...eventArgs) }
}
}
// the API
// block bool (write only) set to true will block events for
// rate but only if not already blocked. Set to false to clear any blocking
// blocked returns (read only) bool for current blocking state
// onlimitedevent read/write function to call on event. If not a function then clears the event
// event read only gets the event to attach to the listener
const API = {
set block(value) {
if (value && !blocked) {
handle = setTimeout(()=>API.block = false, rate);
blocked = true;
} else if (!value && blocked) {
clearTimeout(handle); // incase unblock is from outside
blocked = false
}
},
get blocked() { return blocked },
set onlimitedevent(func) { event = typeof func === "function" ? func : undefined },
get onlimitedevent() { return event },
get event() { return eventHandler },
}
API.onlimitedevent = eventFunc;
return API;
};
})();
const clicker = rateLimit(() => {log("clicked")});
document.body.addEventListener("click", clicker.event);
log("Ready, click me to test.");
function log(...data) { out.appendChild(Object.assign(document.createElement("div"),{textContent : data.toString()})) }
<code id="out"></code>