the Vanilla js variant with cancellable wrap
Note that this solution doesn't require you to modify an external debounce
function or to even use an external one. The logic is done in a wrapepr function. Debounce code provided.
The easiest way to allow to cancel an already called function within its debounce period is to call it from a cancelable wrap. Really just add 3 lines of code and an optional condition.
const doTheThingAfterADelayCancellable = debounce((filter, abort) => {
if (abort) return
// here goes your code...
// or call the original function here
}, /*debounce delay*/500)
function onFilterChange(filter) {
let abort = false
if (filter.length < 3) { // your abort condition
abort = true
}
// doTheThingAfterADelay(filter) // before
doTheThingAfterADelayCancellable(filter, abort) // new wrapped debounced call
}
You cancel it by calling it again with abort = true
.
The way it works is that it clears the previous timeout fn and sets a new one as it always does, but now with if (true) return
path.
You can also do it manually from another code...
doTheThingAfterADelayCancellable(null, true)
...or wrap it and call with cancelBounce()
function cancelBounce() {
doTheThingAfterADelayCancellable(null, true)
}
For reference, this is your classic debounce
function taken from Underscore
. It remains intact in my example.
// taken from Underscore.js
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
export function debounce(func, wait, immediate) {
let timeout
return function() {
let context = this, args = arguments
let later = function() {
timeout = null
if (!immediate) func.apply(context, args)
}
let callNow = immediate && !timeout
clearTimeout(timeout)
timeout = setTimeout(later, wait)
if (callNow) func.apply(context, args)
}
}