Debouncing is a technique used to control how many times we allow a function to be executed over time. When a JavaScript function is debounced with a wait time of X milliseconds, it must wait until after X milliseconds have elapsed since the debounced function was last called. You almost certainly have encountered debouncing in your daily lives before — when entering an elevator. Only after X duration of not pressing the "Door open" button (the debounced function not being called) will the elevator door actually close (the callback function is executed).
Implement a debounce
function which accepts a callback function and a wait
duration. Calling debounce()
returns a function which has debounced invocations of the callback function following the behavior described above.
"Debounce, along with throttle, are among the most common front end interview questions; it's the front end equivalent of inverting a binary tree" - GreatFrontEnd.
1let i = 0;2function increment() {3 i++;4}5const debouncedIncrement = debounce(increment, 100);6
7// t = 0: Call debouncedIncrement().8debouncedIncrement(); // i = 09
10// t = 50: i is still 0 because 100ms have not passed.11
12// t = 100: increment() was invoked and i is now 1.
debouncedIncrement()
is called multiple times.
1let i = 0;2function increment() {3 i++;4}5const debouncedIncrement = debounce(increment, 100);6
7// t = 0: Call debouncedIncrement().8debouncedIncrement(); // i = 09
10// t = 50: i is still 0 because 100ms have not passed.11// Call debouncedIncrement() again.12debouncedIncrement(); // i = 013
14// t = 100: i is still 0 because it has only15// been 50ms since the last debouncedIncrement() at t = 50.16
17// t = 150: Because 100ms have passed since18// the last debouncedIncrement() at t = 50,19// increment was invoked and i is now 1 .
I came across this code challenge some time ago, I couldn't provide a fully working solution from the first attemp. It was especially hard during the interview while writing it in Microsoft Docs without access to the console to run the code. Apparently I rely too hard on console.log during the implementation process. My second attempt to crack it was on GreatFrontend where I took this challenge again and finally was able to provide a working solution.
Let's have a quick peek at the code:
1/**2 * @param {Function} func3 * @param {number} wait4 * @return {Function}5 */6export default function debounce(func, wait = 0) {7 let timeoutID = null;8
9 return function (...args) {10 const context = this;11
12 clearTimeout(timeoutID);13
14 timeoutID = setTimeout(() => {15 timeoutID = null;16
17 func.apply(context, args);18 }, wait);19 };20}
To begin with, inside the debounce
function, a closure is returned. This returned function can be called with any number of arguments (...args)
. When this returned function is called, it resets a timer (tracked by timeoutID
) using clearTimeout
, effectively canceling any previous scheduled executions of the func
. Then, a new timer is set using setTimeout
, delaying the execution of func
by the specified wait
duration.
After the wait
duration has passed without any subsequent calls to the returned function, the scheduled setTimeout
callback is triggered. This callback sets timeoutID
back to null
and invokes the original func
with the provided arguments (args
). This mechanism ensures that the func
is executed only after the rapid succession of calls has stopped, preventing unnecessary or frequent invocations and helping to manage resource-intensive operations more efficiently.
Sign up to get updates when I write something new. No spam ever.
Subscribe to my Newsletter